diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..11f6bd6e --- /dev/null +++ b/.clang-format @@ -0,0 +1,80 @@ +# Copyright (C) 2016 Olivier Goffart +# +# You may use this file under the terms of the 3-clause BSD license. +# See the file LICENSE from this package for details. + +# This is the clang-format configuration style to be used by Qt, +# based on the rules from https://wiki.qt.io/Qt_Coding_Style and +# https://wiki.qt.io/Coding_Conventions + +--- +# Webkit style was loosely based on the Qt style +BasedOnStyle: WebKit + +Standard: Cpp11 + +# Leave the line breaks up to the user. +# Note that this may be changed at some point in the future. +ColumnLimit: 120 +# How much weight do extra characters after the line length limit have. +PenaltyExcessCharacter: 400 + +# Disable reflow of qdoc comments: indentation rules are different. +# Translation comments are also excluded. +CommentPragmas: "^!|^:" + +# We want a space between the type and the star for pointer types. +PointerBindsToType: false + +# We use template< without space. +SpaceAfterTemplateKeyword: false + +# We want to break before the operators, but not before a '='. +BreakBeforeBinaryOperators: NonAssignment + +BreakBeforeBraces: Allman +# Braces are usually attached, but not after functions or class declarations. +#BreakBeforeBraces: Custom +#BraceWrapping: +# AfterClass: true +# AfterControlStatement: false +# AfterEnum: false +# AfterFunction: true +# AfterNamespace: false +# AfterObjCDeclaration: false +# AfterStruct: true +# AfterUnion: true +# BeforeCatch: false +# BeforeElse: false +# IndentBraces: false + +# When constructor initializers do not fit on one line, put them each on a new line. +ConstructorInitializerAllOnOneLineOrOnePerLine: true +# Indent initializers by 4 spaces +ConstructorInitializerIndentWidth: 4 + +# Indent width for line continuations. +ContinuationIndentWidth: 8 + +# No indentation for namespaces. +NamespaceIndentation: None + +# Horizontally align arguments after an open bracket. +# The coding style does not specify the following, but this is what gives +# results closest to the existing code. +AlignAfterOpenBracket: true +AlwaysBreakTemplateDeclarations: true + +# Ideally we should also allow less short function in a single line, but +# clang-format does not handle that. +AllowShortFunctionsOnASingleLine: Inline + +# The coding style specifies some include order categories, but also tells to +# separate categories with an empty line. It does not specify the order within +# the categories. Since the SortInclude feature of clang-format does not +# re-order includes separated by empty lines, the feature is not used. +SortIncludes: false + +# macros for which the opening brace stays attached. +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] + diff --git a/.gitignore b/.gitignore index 880a46ac..83d75fd9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,26 +9,33 @@ src/multichain-cli src/multichain-util src/multichaind src/multichaind-cold +src/multichain-cli-wdebug +src/multichain-util-wdebug +src/multichaind-wdebug +src/multichaind-cold-wdebug src/test/test_bitcoin src/qt/test/test_bitcoin-qt +local/* +openssl* + # autoreconf Makefile.in aclocal.m4 autom4te.cache/ -build-aux/config.guess -build-aux/config.sub -build-aux/depcomp -build-aux/install-sh -build-aux/ltmain.sh -build-aux/m4/libtool.m4 -build-aux/m4/lt~obsolete.m4 -build-aux/m4/ltoptions.m4 -build-aux/m4/ltsugar.m4 -build-aux/m4/ltversion.m4 -build-aux/missing -build-aux/compile -build-aux/test-driver +**/build-aux/config.guess +**/build-aux/config.sub +**/build-aux/depcomp +**/build-aux/install-sh +**/build-aux/ltmain.sh +**/build-aux/m4/libtool.m4 +**/build-aux/m4/lt~obsolete.m4 +**/build-aux/m4/ltoptions.m4 +**/build-aux/m4/ltsugar.m4 +**/build-aux/m4/ltversion.m4 +**/build-aux/missing +**/build-aux/compile +**/build-aux/test-driver config.log config.status configure @@ -115,7 +122,31 @@ qa/pull-tester/run-bitcoind-for-test.sh qa/pull-tester/tests-config.sh qa/pull-tester/cache/* qa/pull-tester/test.*/* +test_filters.sh !src/leveldb*/Makefile /doc/doxygen/ + +# V8 +v8build + +# Eclipse CDT +.autotools +.cproject +.project +.settings/ + +# Qt Creator +*.user +*.config +*.creator +*.files +*.includes + +.idea/ +cmake-build-debug +x86_64-w64-mingw32 +*.dll + +!depends/clang_fix.config diff --git a/README.md b/README.md index 8a2c8dc6..56e8ce8a 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,7 @@ MultiChain [MultiChain](http://www.multichain.com/) is an open source platform for private blockchains, which offers a rich set of features including extensive configurability, rapid deployment, permissions management, native assets and data streams. Although it is designed to enable private blockchains, MultiChain provides maximal compatibility with the bitcoin ecosystem, including the peer-to-peer protocol, transaction/block formats and [Bitcoin Core](https://bitcoin.org/en/bitcoin-core/) APIs/runtime parameters. -MultiChain is licensed under the GPLv3 open source license. We also offer commercial licenses for those wishing to integrate MultiChain into a proprietary software solution. - - Copyright (c) 2014-2017 Coin Sciences Ltd + Copyright (c) 2014-2019 Coin Sciences Ltd License: GNU General Public License version 3, see COPYING Portions copyright (c) 2009-2016 The Bitcoin Core developers @@ -14,29 +12,47 @@ MultiChain is licensed under the GPLv3 open source license. We also offer commer System requirements ------------------- -These compilation instructions have been tested on Ubuntu 14.04 x64 only. +These compilation instructions have been tested on Ubuntu 16.04 x64 (xenial) and Ubuntu 18.04 x64 (bionic) only. C++ compilers are memory-hungry, so it is recommended to have at least 1 GB of memory available when compiling MultiChain. With less memory, compilation may take much longer due to swapfile thrashing. -Linux Build Notes (on Ubuntu 14.04 x64) +Linux Build Notes (on Ubuntu 16.04 x64 or later) ================= Install dependencies -------------------- sudo apt-get update - sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils - sudo apt-get install libboost-all-dev - sudo apt-get install git - sudo apt-get install software-properties-common + sudo apt-get install -y software-properties-common + sudo apt-get install -y build-essential libtool autotools-dev automake pkg-config libssl-dev git python python-pip sudo add-apt-repository ppa:bitcoin/bitcoin - sudo apt-get update - sudo apt-get install libdb4.8-dev libdb4.8++-dev + sudo apt-get install -y libdb4.8-dev libdb4.8++-dev + sudo pip install pathlib2 + +MultiChain requires Boost version no later than 1.65. + + sudo apt-get install -y libboost1.65-all-dev + +Clone MultiChain +---------------- + + git clone https://github.com/MultiChain/multichain.git + +Prepare to download or build V8 +------------------- + + cd multichain + set MULTICHAIN_HOME=$(pwd) + mkdir v8build + cd v8build + +You can use pre-built headers and binaries of Google's V8 JavaScript engine by downloading and expanding [linux-v8.tar.gz](https://github.com/MultiChain/multichain-binaries/raw/master/linux-v8.tar.gz) in the current directory. If, on the other hand, you prefer to build the V8 component yourself, please follow the instructions in [V8.md](/V8.md/). Compile MultiChain for Ubuntu (64-bit) ----------------------------- + cd $MULTICHAIN_HOME ./autogen.sh ./configure make @@ -49,98 +65,13 @@ Notes * The release is built with GCC after which `strip multichaind` strings the debug symbols, which reduces the executable size by about 90%. -Windows Build Notes (on Ubuntu 14.04 x64) +Windows Build Notes ===================== -Install dependencies --------------------- - - sudo apt-get update - sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils - sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev g++-mingw-w64-x86-64 mingw-w64-x86-64-dev curl - sudo apt-get install libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev - sudo apt-get install git - sudo add-apt-repository ppa:bitcoin/bitcoin - sudo apt-get update - sudo apt-get install libdb4.8-dev libdb4.8++-dev - -Compile MultiChain for Windows (64-bit) ------------------------------- - - ./autogen.sh - cd depends - make HOST=x86_64-w64-mingw32 -j4 - cd .. - ./configure --prefix=`pwd`/depends/x86_64-w64-mingw32 --enable-cxx --disable-shared --enable-static --with-pic - make - -Notes ------ - -* This will build `multichaind.exe`, `multichain-cli.exe` and `multitchain-util.exe` in the `src` directory. +Please see the instructions in [win.md](/win.md/) to build MultiChain for use with Windows. Mac Build Notes (on MacOS Sierra) ================ -Install dependencies --------------------- - - Install XCode and XCode command line tools - Install git from git-scm - Install brew (follow instructions on brew.sh) - brew install autoconf automake berkeley-db4 libtool boost openssl pkg-config rename - -on MacOS High Sierra - - brew uninstall boost - brew install boost@1.57 - brew link boost@1.57 --force - -Prepare for static linking --------------------------- -Apple does not support statically linked binaries as [documented here](https://developer.apple.com/library/content/qa/qa1118/_index.html), however, it is convenient for end-users to launch a binary without having to first install brew, a third-party system designed for developers. - -To create a statically linked MultiChain which only depends on default MacOS dylibs, the following steps are taken: - -1. Hide the brew boost dylibs from the build system: - rename -e 's/.dylib/.dylib.hidden/' /usr/local/opt/boost/lib/*.dylib - -2. Hide the brew berekley-db dylibs from the build system: - rename -e 's/.dylib/.dylib.hidden/' /usr/local/opt/berkeley-db\@4/lib/*.dylib - -3. Hide the brew openssl dylibs from the build system: - rename -e 's/.dylib/.dylib.hidden/' /usr/local/opt/openssl/lib/*.dylib - -The default brew cookbook for berkeley-db and boost builds static libraries, but the default cookbook for openssl only builds dylibs. - -3. Tell brew to build openssl static libraries: - brew edit openssl - In 'def configure_args' change 'shared' to 'no-shared' - brew install openssl --force - - - -Compile MultiChain for Mac (64-bit) --------------------------- - - export LDFLAGS=-L/usr/local/opt/openssl/lib - export CPPFLAGS=-I/usr/local/opt/openssl/include - ./autogen.sh - ./configure --with-gui=no --with-libs=no --with-miniupnpc=no - make - -Clean up --------- - - rename -e 's/.dylib.hidden/.dylib/' /usr/local/opt/berkeley-db\@4/lib/*.dylib.hidden - rename -e 's/.dylib.hidden/.dylib/' /usr/local/opt/boost/lib/*.dylib.hidden - rename -e 's/.dylib.hidden/.dylib/' /usr/local/opt/openssl/lib/*.dylib.hidden - brew edit openssl - In 'def configure_args' change 'no-shared' to 'shared' - -Notes ------ - -* This will build `multichaind`, `multichain-cli` and `multichain-util` in the `src` directory. - +Please see the instructions in [mac.md](/mac.md/) to build MultiChain for use with MacOS. diff --git a/V8.md b/V8.md new file mode 100644 index 00000000..8842b5ec --- /dev/null +++ b/V8.md @@ -0,0 +1,78 @@ +# Fetching, Building and Installing V8 (On Ubuntu 16.04 x64 or later) + +The following instructions fetch the Google V8 JavaScript engine to your local machine and configure it to create static libraries in the location where the MultiChain build system expects to find them. + +MultiChain uses V8 version 6.8, and requires at least 4 GB of RAM to build in a reasonable time. It will not build at all with less than 2 GB RAM. + +## Install dependencies + + sudo apt-get update + sudo apt-get -y install git python pkg-config build-essential + +## Clone Google's depot_tools + +Google's [depot_tools](http://dev.chromium.org/developers/how-tos/install-depot-tools) are used by the Google build system to manage Git checkouts. + + git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git + export PATH=${PATH}:$(pwd)/depot_tools + +## Fetch V8 + +The following commands check out V8 and select the branch used by MultiChain. Please note that this step downloads about 2 GB of data, and can take a long time (30 minutes or more). + + gclient + fetch v8 + cd v8 + git checkout 6.8.290 + +## Configure and build V8 + +The V8 build system currently uses a proprietary version of the Ninja build system, called GN. It is part of the `depot_tools` installed earlier. + + find . -name BUILD.gn -exec sed -i '/exe_and_shlib_deps/d' {} \; + tools/dev/v8gen.py x64.release + RELEASE=out.gn/x64.release + cat > $RELEASE/args.gn << END + is_debug = false + target_cpu = "x64" + is_component_build = false + v8_static_library = true + use_custom_libcxx = false + use_custom_libcxx_for_host = false + END + +The selected release of the V8 sources requires relaxing two compiler checks to prevent compilation errors. + +- Open the file `build/config/compiler/BUILD.gn` in your favorite editor. +- Locate the folllowing lines (currently at **1464**): + + if (is_clang) { + cflags += [ + +- Add the following two lines to **the end** of the block and save the file: + + "-Wno-defaulted-function-deleted", + "-Wno-null-pointer-arithmetic", + +Build the V8 libraries: + + gn gen $RELEASE + ninja -C $RELEASE v8 d8 + +Create an additional library embedding the V8 initial snapshot blobs: + + sudo easy_install pip + pip install pathlib2 + cd $RELEASE + python $MULTICHAIN_HOME/depends/v8_data_lib.py + + diff --git a/V8_mac.md b/V8_mac.md new file mode 100644 index 00000000..c5ce21c8 --- /dev/null +++ b/V8_mac.md @@ -0,0 +1,65 @@ +# Fetching, Building and Installing V8 (on MacOS Sierra) + +The following instructions fetch the Google V8 JavaScript engine to your local machine and configure it to create static libraries in the location where the MultiChain build system expects to find them. + +MultiChain uses V8 version 6.8, and requires at least 4 GB of RAM to build in a reasonable time. It will not build at all with less than 2 GB RAM. + +## Clone Google's depot_tools + +Google's [depot_tools](https://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up) are used by the Google build system to manage Git checkouts. + + git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git + cd depot_tools + git checkout af2ffd933d0e6d8b5bd8c48be7a2b2d568a5eea2 + cd .. + export PATH=${PATH}:$(pwd)/depot_tools + +## Fetch V8 + +The following commands check out V8 and select the branch used by MultiChain. Please note that this step downloads about 2 GB of data, and can take a long time (30 minutes or more). + + gclient + fetch v8 + cd v8 + git checkout 6.8.290 + +## Configure and build V8 + +The V8 build system currently uses a proprietary version of the Ninja build system, called GN. It is part of the `depot_tools` installed earlier. + + find . -name BUILD.gn -exec sed -i bak '/exe_and_shlib_deps/d' {} \; + pushd base/trace_event/common + git checkout 211b3ed9d0481b4caddbee1322321b86a483ca1f + popd + RELEASE=out.gn/x64.release + gn gen $RELEASE --args='is_debug=false target_cpu="x64" v8_static_library=true is_component_build=false use_custom_libcxx=false use_custom_libcxx_for_host=false' + +The selected release of the V8 sources requires relaxing two compiler checks to prevent compilation errors. + +- Open the file `build/config/compiler/BUILD.gn` in your favorite editor. + +- Locate the following lines (currently at **1469**): + + if (is_clang) { + cflags += [ + +- Add the following two lines to **the end of the block** and save the file: + + "-Wno-defaulted-function-deleted", + "-Wno-null-pointer-arithmetic", + +Build the V8 libraries: + + gn gen $RELEASE + ninja -C $RELEASE v8 d8 + +Create an additional library embedding the V8 initial snapshot blobs: + + brew install python@2 + pip install pathlib2 + cd $RELEASE + python $MULTICHAIN_HOME/depends/v8_data_lib.py + +### Note + +The environment variable `$MULTICHAIN_HOME` should point to the parent of the V8 build folder (see [mac.md](mac.md) for details). diff --git a/V8_win.md b/V8_win.md new file mode 100644 index 00000000..7fe24272 --- /dev/null +++ b/V8_win.md @@ -0,0 +1,69 @@ +# Prerequisites + +Building Google's V8 on Windows can only be done inside a Windows command prompt (`cmd.exe`), and not in PowerShell or any Unix-like shell (e.g. Cygwin, MSYS, Git Bash, WSL). + +It requires the following software: + +- [Visual Studio 2017](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) (community edition acceptable) +- [Python 2.7](https://www.python.org/ftp/python/2.7.15/python-2.7.15.amd64.msi) +- [Git](https://github.com/git-for-windows/git/releases/download/v2.19.1.windows.1/Git-2.19.1-64-bit.exe) + +- Add the Debugging Tools For Windows: + + - Control Panel → Programs → Programs and Features → Select the "Windows Software Development Kit" → Change → Change → Check "Debugging Tools For Windows" → Change + +The steps described in this document have to be executed in a shell configured for Visual Studio 64 bit (Start / Visual Studio 2017 / x64 Native Tools Command Prompt). + +# Fetching, Building and Installing V8 + +The following instructions fetch the Google V8 JavaScript engine to your local machine, configure it to create static libraries, and builds them. + +MultiChain uses V8 version 6.8.290, and requires at least 4 GB of RAM to build in a reasonable time. It will not build at all with less than 2 GB RAM. + +## Get Google's `depot_tools` + +Google's [`depot_tools`](http://dev.chromium.org/developers/how-tos/install-depot-tools) are used to manage Git checkouts and the build system. + +- Download https://storage.googleapis.com/chrome-infra/depot_tools.zip and expand the archive to the current directory. +- Edit the PATH environment variable and insert the full path of `depot_tools` **before** the installed `python.exe` and `git.exe`. +- Set the environment variable `DEPOT_TOOLS_WIN_TOOLCHAIN=0`. + +## Fetch V8 + +The following commands check out V8 and select the branch used by MultiChain. Please note that this step downloads about 2 GB of data, and can take a long time (30 minutes or more). + + gclient + +Execute `where python` to ensure that `depot_tools\python.bat` indeed comes *before* `python.exe` + + fetch v8 + cd v8 + git checkout 6.8.290 + pushd base\trace_event\common + git checkout 211b3ed9d0481b4caddbee1322321b86a483ca1f + popd + +## Configure and build V8 + +The V8 build system currently uses a proprietary version of the Ninja build system, called GN. It is part of the `depot_tools` installed earlier. + +- Edit the file `BUILD.gn` and remove all lines containing `exe_and_shlib_deps` (8 occurrences) +- Edit the following files and remove all lines containing `exe_and_shlib_deps`: + - BUILD.gn (8 occurrences) + - test\cctest\BUILD.gn (2 occurrences) + - test\inspector\BUILD.gn (1 occurrence) + - test\mkgrokdump\BUILD.gn (1 occurrence) + - test\unittests\BUILD.gn (2 occurrences) + +- Execute `set RELEASE=out.gn\x64.release` +- Execute `gn args %RELEASE%`, insert the following content, save and exit Notepad: + + is_debug = false + target_cpu = "x64" + v8_static_library = true + is_component_build = false + is_clang = false + v8_enable_object_print = true + treat_warnings_as_errors = false + +- Execute `ninja -C %RELEASE% v8 d8` diff --git a/configure.ac b/configure.ac index 224e463f..9f5ebec3 100644 --- a/configure.ac +++ b/configure.ac @@ -81,6 +81,12 @@ AC_ARG_ENABLE([wallet], [enable_wallet=$enableval], [enable_wallet=yes]) +AC_ARG_ENABLE([enterprise], + [AS_HELP_STRING([--enable-enterprise], + [enable enterprise (default is no)])], + [enable_enterprise=$enableval], + [enable_enterprise=no]) + AC_ARG_WITH([miniupnpc], [AS_HELP_STRING([--with-miniupnpc], [enable UPNP (default is yes if libminiupnpc is found)])], @@ -701,12 +707,12 @@ else fi fi -CFLAGS_TEMP="$CFLAGS" +CPPFLAGS_TEMP="$CPPFLAGS" LIBS_TEMP="$LIBS" -CFLAGS="$CFLAGS $SSL_CFLAGS $CRYPTO_CFLAGS" -LIBS="$LIBS $SSL_LIBS $CRYPTO_LIBS" +CPPFLAGS="$CPPFLAGS $SSL_CFLAGS" +LIBS="$LIBS $SSL_LIBS" AC_CHECK_HEADER([openssl/ec.h],, AC_MSG_ERROR(OpenSSL ec header missing),) -CFLAGS="$CFLAGS_TEMP" +CPPFLAGS="$CPPFLAGS_TEMP" LIBS="$LIBS_TEMP" BITCOIN_QT_PATH_PROGS([PROTOC], [protoc],$protoc_bin_path) @@ -757,6 +763,17 @@ else AC_MSG_RESULT(no) fi +dnl enable enterprise +AC_MSG_CHECKING([if enterprise should be enabled]) +if test x$enable_enterprise != xno; then + AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED([ENABLE_ENTERPRISE],[1],[Define to 1 to enable enterprise functions]) + +else + AC_MSG_RESULT(no) +fi + + dnl enable upnp support AC_MSG_CHECKING([whether to build with support for UPnP]) if test x$have_miniupnpc = xno; then @@ -847,6 +864,7 @@ AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin]) AM_CONDITIONAL([BUILD_DARWIN], [test x$BUILD_OS = xdarwin]) AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows]) AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes]) +AM_CONDITIONAL([ENABLE_ENTERPRISE],[test x$enable_enterprise = xyes]) AM_CONDITIONAL([ENABLE_TESTS],[test x$use_tests = xyes]) AM_CONDITIONAL([ENABLE_QT],[test x$bitcoin_enable_qt = xyes]) AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$use_tests$bitcoin_enable_qt_test = xyesyes]) diff --git a/depends/clang_fix.config b/depends/clang_fix.config new file mode 100644 index 00000000..09cdf0b1 --- /dev/null +++ b/depends/clang_fix.config @@ -0,0 +1,10 @@ +[ + { + "file": "build/config/compiler/BUILD.gn", + "config": "default_warnings", + "flags": [ + "-Wno-defaulted-function-deleted", + "-Wno-null-pointer-arithmetic" + ] + } +] diff --git a/depends/clang_fix.py b/depends/clang_fix.py new file mode 100644 index 00000000..32ea0fb7 --- /dev/null +++ b/depends/clang_fix.py @@ -0,0 +1,61 @@ +import itertools +import logging +import shutil +import sys +from argparse import ArgumentParser, FileType + +from pathlib2 import Path + +module_name = Path(__file__).stem +logger = logging.getLogger(module_name) + +TEMPORARY_FILE = "temporary_file" + + +def process_config_file(options): + lines = zip(itertools.count(), options.input.readlines()) + default_warnings = next(idx for idx, line in lines if line.startswith('config("default_warnings") {')) + in_clang_block = next(idx for idx, line in lines[default_warnings:] if line.startswith(' if (is_clang) {')) + cflags_block = next(idx for idx, line in lines[in_clang_block:] if line.startswith(' cflags += [')) + lines[cflags_block+1:cflags_block+1] = [ + (0, ' "-Wno-defaulted-function-deleted",\n'), + (0, ' "-Wno-null-pointer-arithmetic",\n'), + (0, ' "-Wno-class-memaccess",\n'), + ] + + if options.inplace: + with open(TEMPORARY_FILE, 'w') as f: + for idx, line in lines: + f.write(line) + shutil.move(options.input.name, options.input.name + options.inplace) + shutil.move(TEMPORARY_FILE, options.input.name) + else: + for idx, line in lines: + print(line.rstrip()) + + +def get_options(): + parser = ArgumentParser(description="Add clang flags to GN compiler config file") + parser.add_argument("-i", "--inplace", metavar="EXT", action="store", const=".bak", nargs="?", default="", + help="replace file (default: '%(const)s' if given, output to stdout if not)") + parser.add_argument("input", type=FileType(), nargs="?", default="build/config/compiler/BUILD.gn", + help="GN config file name (default: %(default)s)") + + options = parser.parse_args() + + logger.info("{} - {}".format(module_name, parser.description)) + logger.info(" In-place: {!r}".format(options.inplace)) + logger.info(" In-file: {!r}".format(options.input.name)) + + return options + + +def main(): + logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(asctime) %(levelname)-7s %(message)s") + options = get_options() + process_config_file(options) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/depends/clang_fix2.py b/depends/clang_fix2.py new file mode 100644 index 00000000..4d32321b --- /dev/null +++ b/depends/clang_fix2.py @@ -0,0 +1,79 @@ +import itertools +import json +import logging +import os +import shutil +import sys +from argparse import ArgumentParser, FileType + +from pathlib2 import Path + +module_name = Path(__file__).stem +logger = logging.getLogger(module_name) + +TEMPORARY_FILE = "temporary_file" + + +def process_config_file(options, spec): + logger.debug("process_config_file {}".format(spec["file"])) + config_name = str(spec["config"]) + lines = zip(itertools.count(), [line.rstrip() for line in open(spec["file"]).readlines()]) + config = next(idx for idx, line in lines if line.startswith('config("{}") {{'.format(config_name))) + in_clang_block = next(idx for idx, line in lines[config:] if line.startswith(' if (is_clang) {')) + cflags_block = next(idx for idx, line in lines[in_clang_block:] if line.startswith(' cflags += [')) + lines[cflags_block+1:cflags_block+1] = [(0, ' "{}",'.format(flag)) for flag in spec["flags"]] + + if options.inplace: + with open(TEMPORARY_FILE, 'w') as f: + for idx, line in lines: + f.write(line + '\n') + shutil.move(spec["file"], spec["file"] + options.inplace) + shutil.move(TEMPORARY_FILE, spec["file"]) + else: + for idx, line in lines: + print(line.rstrip()) + + +def get_options(): + mc_path_default = str(Path.home() / "multichain") + parser = ArgumentParser(description="Add clang flags to GN compiler config file") + parser.add_argument("-v", "--verbose", action="store_true", help="write debug messages to log") + parser.add_argument("-m", "--multichain", metavar="DIR", default=mc_path_default, + help="MultiChain path prefix (default: %(default)s)") + parser.add_argument("-i", "--inplace", metavar="EXT", action="store", const=".bak", nargs="?", default="", + help="replace file (default: '%(const)s' if given, output to stdout if not)") + parser.add_argument("-c", "--config", metavar="FILE", default="clang_fix.config", + help="configuration file name (default: %(default)s)") + + options = parser.parse_args() + + if options.verbose: + logger.setLevel(logging.DEBUG) + + if not Path(options.multichain).exists(): + parser.error("{!r}: MultiChain path does not exist".format(options.multichain)) + + config_file = Path(options.config) + if not config_file.is_absolute(): + options.config = str(Path(__file__).parent / config_file) + + logger.info("{} - {}".format(module_name, parser.description)) + logger.info(" MultiChain: {!r}".format(options.multichain)) + logger.info(" In-place: {!r}".format(options.inplace)) + logger.info(" Config: {!r}".format(options.config)) + + return options + + +def main(): + logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(asctime)s %(levelname)-7s %(message)s") + options = get_options() + specs = json.load(open(options.config)) + os.chdir(str(Path(options.multichain) / "v8build" / "v8")) + for spec in specs: + process_config_file(options, spec) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/depends/v8_data_lib.py b/depends/v8_data_lib.py new file mode 100644 index 00000000..5a28451b --- /dev/null +++ b/depends/v8_data_lib.py @@ -0,0 +1,99 @@ +import logging +import os +import sys +from argparse import ArgumentParser +from itertools import chain +from subprocess import call + +from pathlib2 import Path + +logger = logging.getLogger(str(Path(__file__).stem)) +asm = """ +section .rodata + +global {PREFIX}_start; +global {PREFIX}_end; + +{PREFIX}_start: incbin "{NAME}" +{PREFIX}_end: +{PREFIX}_size: dd {PREFIX}_end-{PREFIX}_start +""" + + +def get_bin_type(platform): + if platform == "darwin": + bin_type = ("macho64", ".o", "lib{}.a") + elif platform == "win32": + bin_type = ("win64", ".obj", "{}.lib") + else: + bin_type = ("elf64", ".o", "lib{}.a") + return bin_type + + +def process_bin_file(filepath, platform): + logger.debug("process_bin_file(filepath={})".format(filepath)) + assert filepath.exists() + arch, obj_suffix, _lib_pattern = get_bin_type(platform) + obj_name = filepath.with_suffix(obj_suffix).name + obj_path = Path("obj") / obj_name + symbol_prefix = "_binary_{}_{}".format(filepath.stem, filepath.suffix[1:]) + if platform == "darwin": + symbol_prefix = '_' + symbol_prefix + script = asm.format(PREFIX=symbol_prefix, NAME=filepath.name) + script_file = filepath.with_suffix(".s").name + with open(script_file, 'w') as f: + f.write(script) + cmd = ["nasm", "-f", arch, "-o", str(obj_path), script_file] + logger.info(' '.join(cmd)) + call(cmd) + return obj_name + + +def process_bin_files(platform): + cwd = Path.cwd() + obj_names = [] + for f in chain(cwd.glob("*.bin"), cwd.glob("*.dat")): + obj_names.append(process_bin_file(f, platform)) + os.chdir("obj") + _arch, _obj_suffix, lib_pattern = get_bin_type(platform) + ar = "x86_64-w64-mingw32-ar" if platform == "win32" else "ar" + cmd = [ar, "rvs", lib_pattern.format("v8_data")] + cmd += obj_names + logger.info(' '.join(cmd)) + call(cmd) + + +def get_options(): + mc_path_default = str(Path.home() / "multichain") + parser = ArgumentParser(description="Build v8_data.lib from .bin and .dat files") + parser.add_argument("-v", "--verbose", action="store_true", help="print debug messages to log") + parser.add_argument("-m", "--multichain", metavar="DIR", default=mc_path_default, + help="MultiChain path prefix (default: %(default)s)") + parser.add_argument("-o", "--platform", default=sys.platform, + help="override platform definition (default: %(default)s)") + + options = parser.parse_args() + + if options.verbose: + logger.setLevel(logging.DEBUG) + + if not Path(options.multichain).exists(): + parser.error("{!r}: MultiChain path does not exist".format(options.multichain)) + + logger.info("{} - {}".format(logger.name, parser.description)) + logger.info(" Path: {}".format(options.multichain)) + logger.info(" Platform: {}".format(options.platform)) + + return options + + +def main(): + logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(asctime)s %(levelname)-7s %(message)s") + options = get_options() + os.chdir(str(Path(options.multichain) / "v8build" / "v8" / "out.gn" / "x64.release")) + process_bin_files(options.platform) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/mac.md b/mac.md new file mode 100644 index 00000000..efd999c4 --- /dev/null +++ b/mac.md @@ -0,0 +1,98 @@ +# Mac Build Notes (on MacOS Sierra) + +## Install XCode and XCode command line tools + +- Test if XCode command line tools are installed: + + xcode-select -p + +- If the command does not print the location of the XCode command-line tools successfully: + + xcode-select --install + +- Select XCode locatioh: + + sudo xcode-select -s + +- Install brew (follow instructions on [brew.sh](https://brew.sh/)) + +## Clone MultiChain +Install git from git-scm, then + + git clone https://github.com/MultiChain/multichain.git + + +## Prepare to download or build V8 + + cd multichain + set MULTICHAIN_HOME=$(pwd) + mkdir v8build + cd v8build + + +You can use pre-built headers and binaries of Google's V8 JavaScript engine by downloading and expanding [macos-v8.tar.gz](https://github.com/MultiChain/multichain-binaries/raw/master/macos-v8.tar.gz) in the current directory. If, on the other hand, you prefer to build the V8 component yourself, please follow the instructions in [V8_mac.md](/V8_mac.md/). + +## Install dependencies + + brew install autoconf automake berkeley-db4 libtool boost@1.57 pkg-config rename python@2 nasm + export LDFLAGS=-L/usr/local/opt/openssl/lib + export CPPFLAGS=-I/usr/local/opt/openssl/include + brew link boost@1.57 --force + +If another Boost version was already installed, then do this: + + brew uninstall boost + brew install boost@1.57 + brew link boost@1.57 --force + +## Prepare for static linking + +Apple does not support statically linked binaries as [documented here](https://developer.apple.com/library/content/qa/qa1118/_index.html), however, it is convenient for end-users to launch a binary without having to first install brew, a third-party system designed for developers. + +To create a statically linked MultiChain which only depends on default MacOS dylibs, the following steps are taken: + +1. Hide the brew boost dylibs from the build system: + + rename -e 's/.dylib/.dylib.hidden/' /usr/local/opt/boost\@1.57/lib/*.dylib + +2. Hide the brew berekley-db dylibs from the build system: + + rename -e 's/.dylib/.dylib.hidden/' /usr/local/opt/berkeley-db\@4/lib/*.dylib + +3. Hide the brew openssl dylibs from the build system: + + rename -e 's/.dylib/.dylib.hidden/' /usr/local/opt/openssl/lib/*.dylib + +The default brew cookbook for berkeley-db and boost builds static libraries, but the default cookbook for openssl only builds dylibs. + +3. Tell brew to build openssl static libraries: + + brew edit openssl + + In 'def install' => 'args =' change 'shared' to 'no-shared' + + brew install openssl --force + + +## Compile MultiChain for Mac (64-bit) + + cd $MULTICHAIN_HOME + ./autogen.sh + ./configure --with-gui=no --with-libs=no --with-miniupnpc=no + make + +## Clean up + + rename -e 's/.dylib.hidden/.dylib/' /usr/local/opt/berkeley-db\@4/lib/*.dylib.hidden + rename -e 's/.dylib.hidden/.dylib/' /usr/local/opt/boost/lib/*.dylib.hidden + rename -e 's/.dylib.hidden/.dylib/' /usr/local/opt/openssl/lib/*.dylib.hidden + brew edit openssl + +In 'def install' => 'args =' change 'no-shared' to 'shared' + +## Notes + +* This will build `multichaind`, `multichain-cli` and `multichain-util` in the `src` directory. diff --git a/src/Makefile.am b/src/Makefile.am index 6305b149..38a6b745 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,17 +1,20 @@ -walDIST_SUBDIRS = secp256k1 +FwalDIST_SUBDIRS = secp256k1 AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) +CXXFLAGS += -std=c++0x +CPPFLAGS += -Wno-deprecated-declarations -Wno-unused-local-typedefs -Wno-unused-result -DBOOST_SYSTEM_NO_DEPRECATED=1 if EMBEDDED_LEVELDB LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/helpers/memenv +LEVELDB_CPPFLAGS += -Wno-deprecated LIBLEVELDB += $(builddir)/leveldb/libleveldb.a LIBMEMENV += $(builddir)/leveldb/libmemenv.a # NOTE: This dependency is not strictly necessary, but without it make may try to build both in parallel, which breaks the LevelDB build system in a race $(LIBLEVELDB): $(LIBMEMENV) -$(LIBLEVELDB) $(LIBMEMENV): +$(LIBLEVELDB) $(LIBMEMENV): @echo "Building LevelDB ..." && $(MAKE) -C $(@D) $(@F) CXX="$(CXX)" \ CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \ OPT="$(CXXFLAGS) $(CPPFLAGS)" @@ -22,6 +25,8 @@ BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BOOST_CPPFLAGS) $(LEVELDB_CPP BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include +LIBBITCOIN_ENTERPRISE=libbitcoin_enterprise.a +LIBBITCOIN_COMMUNITY=libbitcoin_community.a LIBBITCOIN_SERVER=libbitcoin_server.a LIBBITCOIN_WALLET=libbitcoin_wallet.a LIBBITCOIN_COMMON=libbitcoin_common.a @@ -32,6 +37,9 @@ LIBBITCOIN_MULTICHAIN=multichain/libbitcoin_multichain.a LIBBITCOIN_UNIVALUE=univalue/libbitcoin_univalue.a LIBBITCOINQT=qt/libbitcoinqt.a LIBSECP256K1=secp256k1/libsecp256k1.la +if !TARGET_WINDOWS +LIBBITCOIN_V8=libbitcoin_v8.a +endif $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) @@ -44,13 +52,17 @@ EXTRA_LIBRARIES = \ libbitcoin_util.a \ libbitcoin_common.a \ univalue/libbitcoin_univalue.a \ + libbitcoin_enterprise.a \ + libbitcoin_community.a \ libbitcoin_server.a \ libbitcoin_cli.a if ENABLE_WALLET BITCOIN_INCLUDES += $(BDB_CPPFLAGS) EXTRA_LIBRARIES += libbitcoin_wallet.a endif - +if !TARGET_WINDOWS +EXTRA_LIBRARIES += $(LIBBITCOIN_V8) +endif if BUILD_BITCOIN_LIBS lib_LTLIBRARIES = libbitcoinconsensus.la LIBBITCOIN_CONSENSUS=libbitcoinconsensus.la @@ -95,7 +107,9 @@ BITCOIN_CORE_H = \ primitives/transaction.h \ utils/core_io.h \ wallet/crypter.h \ + wallet/dbflat.h \ wallet/db.h \ + wallet/dbwrap.h \ structs/hash.h \ core/init.h \ keys/key.h \ @@ -116,6 +130,7 @@ BITCOIN_CORE_H = \ utils/utilparse.h \ rpc/rpcclient.h \ rpc/rpcprotocol.h \ + rpc/rpcasio.h \ rpc/rpcutils.h \ rpc/rpcwallet.h \ rpc/rpcserver.h \ @@ -166,6 +181,22 @@ obj/build.h: FORCE $(abs_top_srcdir) libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h +libbitcoin_community_a_CPPFLAGS = $(BITCOIN_INCLUDES) +libbitcoin_community_a_SOURCES = \ + community/community.cpp \ + wallet/db.cpp \ + wallet/dbwrap_com.cpp + +if ENABLE_ENTERPRISE +include entMakefile.am +else +libbitcoin_enterprise_a_CPPFLAGS = $(BITCOIN_INCLUDES) +libbitcoin_enterprise_a_SOURCES = \ + community/community.cpp \ + wallet/db.cpp \ + wallet/dbwrap_com.cpp +endif + # server: shared between bitcoind and bitcoin-qt libbitcoin_server_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) libbitcoin_server_a_SOURCES = \ @@ -180,6 +211,7 @@ libbitcoin_server_a_SOURCES = \ protocol/multichaintx.cpp \ protocol/multichainblock.cpp \ custom/custom_server.cpp \ + filters/multichainfilter.cpp \ protocol/relay.cpp \ protocol/handshake.cpp \ chain/merkleblock.cpp \ @@ -213,7 +245,7 @@ libbitcoin_server_a_SOURCES = \ # when wallet enabled libbitcoin_wallet_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_wallet_a_SOURCES = \ - wallet/db.cpp \ + wallet/dbflat.cpp \ wallet/crypter.cpp \ rpc/rpcdump.cpp \ rpc/rpcwallet.cpp \ @@ -225,6 +257,7 @@ libbitcoin_wallet_a_SOURCES = \ rpc/rpcassets.cpp \ rpc/rpcstreams.cpp \ rpc/rpcupgrades.cpp \ + rpc/rpcfilters.cpp \ wallet/wallet.cpp \ wallet/walletcoins.cpp \ wallet/wallettxs.cpp \ @@ -249,6 +282,9 @@ crypto_libbitcoin_crypto_a_SOURCES = \ crypto/sha1.h \ crypto/ripemd160.h + + + # multichain library multichain_libbitcoin_multichain_a_CPPFLAGS = $(BITCOIN_CONFIG_INCLUDES) multichain_libbitcoin_multichain_a_SOURCES = \ @@ -393,7 +429,7 @@ endif $(BOOST_LIBS) \ $(CRYPTO_LIBS) -#bitcoin_tx_SOURCES = bitcoin-tx.cpp +#bitcoin_tx_SOURCES = bitcoin-tx.cpp #bitcoin_tx_CPPFLAGS = $(BITCOIN_INCLUDES) # #bitcoin_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) @@ -405,12 +441,49 @@ endif # MCHN START -# multichaind binary # +V8_ROOT = $(top_builddir)/v8build/v8 +V8_INCLUDE = -I$(V8_ROOT)/include +V8_LIB_DIR = $(V8_ROOT)/out.gn/x64.release/obj +V8_LIBS = \ + -L$(V8_LIB_DIR) -L$(V8_LIB_DIR)/third_party/icu +if !TARGET_DARWIN +V8_LIBS += \ + -Wl,--start-group +endif +V8_LIBS += \ + -lv8_libbase \ + -lv8_libplatform \ + -lv8_base \ + -lv8_external_snapshot \ + -lv8_libsampler \ + -licuuc \ + -licui18n \ + -lv8_data \ + -ldl +if !TARGET_DARWIN +V8_LIBS += \ + -lrt +endif + +libbitcoin_v8_a_SOURCES = \ + v8/v8engine.cpp \ + v8/v8filter.cpp \ + v8/callbacks.cpp \ + v8/v8json_spirit.cpp + +#if TARGET_WINDOWS +#libbitcoin_v8_a_SOURCES += v8/callbacks_win.cpp v8/v8blob.dpp +#endif + +libbitcoin_v8_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(V8_INCLUDE) + +# multichaind binary # multichaind_LDADD = \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UNIVALUE) \ $(LIBBITCOIN_WALLET) \ + $(LIBBITCOIN_ENTERPRISE) \ $(LIBBITCOIN_MULTICHAIN) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) \ @@ -418,26 +491,43 @@ multichaind_LDADD = \ $(LIBMEMENV) \ $(LIBSECP256K1) +if TARGET_WINDOWS + multichaind_LDADD += $(top_builddir)/src/v8_win/build/Release/multichain-v8.lib +else + multichaind_LDADD += $(LIBBITCOIN_V8) +endif + #if ENABLE_WALLET #multichaind_LDADD += libbitcoin_wallet.a #endif -multichaind_SOURCES = multichain/multichaind.cpp rpc/rpclist.cpp chainparams/buildgenesis.cpp -# +multichaind_SOURCES = \ + multichain/multichaind.cpp \ + rpc/rpclist.cpp \ + chainparams/buildgenesis.cpp \ + filters/filtercallback.cpp \ + filters/watchdog.cpp \ + json/json_spirit_writer.cpp if TARGET_WINDOWS -multichaind_SOURCES += multichaind-res.rc +multichaind_SOURCES += filters/filter_win.cpp multichaind-res.rc +else +multichaind_SOURCES += filters/filter.cpp endif multichaind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) -multichaind_CPPFLAGS = $(BITCOIN_INCLUDES) -multichaind_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +multichaind_CPPFLAGS = $(BITCOIN_INCLUDES) $(V8_INCLUDE) +multichaind_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -pthread +if !TARGET_WINDOWS +multichaind_LDFLAGS += $(V8_LIBS) +endif -# multichaind-cold binary # +# multichaind-cold binary # multichaind_cold_LDADD = \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UNIVALUE) \ $(LIBBITCOIN_WALLET) \ + $(LIBBITCOIN_COMMUNITY) \ $(LIBBITCOIN_MULTICHAIN) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) \ @@ -448,7 +538,15 @@ multichaind_cold_LDADD = \ #if ENABLE_WALLET #multichaind_cold_LDADD += libbitcoin_wallet.a #endif -multichaind_cold_SOURCES = multichain/multichaind-cold.cpp core/init-cold.cpp rpc/rpclist-cold.cpp chainparams/buildgenesis.cpp +multichaind_cold_SOURCES = \ + multichain/multichaind-cold.cpp \ + core/init-cold.cpp \ + rpc/rpclist-cold.cpp \ + chainparams/buildgenesis.cpp \ + filters/filter_cold.cpp \ + filters/filtercallback_cold.cpp \ + filters/watchdog.cpp \ + json/json_spirit_writer.cpp # if TARGET_WINDOWS @@ -457,7 +555,7 @@ endif multichaind_cold_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) multichaind_cold_CPPFLAGS = $(BITCOIN_INCLUDES) -multichaind_cold_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +multichaind_cold_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -pthread # multichain-cli binary # multichain_cli_LDADD = \ diff --git a/src/chain/chain.cpp b/src/chain/chain.cpp index 2277bfd2..c5259c9b 100644 --- a/src/chain/chain.cpp +++ b/src/chain/chain.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "chain/chain.h" diff --git a/src/chain/chain.h b/src/chain/chain.h index 8ec3516f..4a7584f4 100644 --- a/src/chain/chain.h +++ b/src/chain/chain.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_CHAIN_H diff --git a/src/chain/checkpoints.cpp b/src/chain/checkpoints.cpp index 84d6f94b..942800a1 100644 --- a/src/chain/checkpoints.cpp +++ b/src/chain/checkpoints.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "chain/checkpoints.h" diff --git a/src/chain/checkpoints.h b/src/chain/checkpoints.h index 1e38c28c..022dbb40 100644 --- a/src/chain/checkpoints.h +++ b/src/chain/checkpoints.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_CHECKPOINTS_H diff --git a/src/chain/merkleblock.cpp b/src/chain/merkleblock.cpp index 6ff4da25..6947fb2b 100644 --- a/src/chain/merkleblock.cpp +++ b/src/chain/merkleblock.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "chain/merkleblock.h" diff --git a/src/chain/merkleblock.h b/src/chain/merkleblock.h index 545c746f..8df6420c 100644 --- a/src/chain/merkleblock.h +++ b/src/chain/merkleblock.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_MERKLEBLOCK_H diff --git a/src/chain/pow.cpp b/src/chain/pow.cpp index 6ebc7283..6c790d07 100644 --- a/src/chain/pow.cpp +++ b/src/chain/pow.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "chain/pow.h" diff --git a/src/chain/pow.h b/src/chain/pow.h index 41d0f066..768356f1 100644 --- a/src/chain/pow.h +++ b/src/chain/pow.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_POW_H diff --git a/src/chain/txmempool.cpp b/src/chain/txmempool.cpp index 62a4df50..3b303c0c 100644 --- a/src/chain/txmempool.cpp +++ b/src/chain/txmempool.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "chain/txmempool.h" diff --git a/src/chain/txmempool.h b/src/chain/txmempool.h index 612ee3ac..c39071ad 100644 --- a/src/chain/txmempool.h +++ b/src/chain/txmempool.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_TXMEMPOOL_H diff --git a/src/chain/undo.h b/src/chain/undo.h index b30c30c1..827f6791 100644 --- a/src/chain/undo.h +++ b/src/chain/undo.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_UNDO_H diff --git a/src/chainparams/buildgenesis.cpp b/src/chainparams/buildgenesis.cpp index 13a8e145..2f9551f1 100644 --- a/src/chainparams/buildgenesis.cpp +++ b/src/chainparams/buildgenesis.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "multichain/multichain.h" @@ -93,7 +93,12 @@ int mc_MultichainParams::Build(const unsigned char* pubkey, int pubkey_size) uint32_t nBits,timestamp; int i; const unsigned char *ptr; - const unsigned char *pubkey_hash=(unsigned char *)Hash160(pubkey,pubkey+pubkey_size).begin(); +// const unsigned char *pubkey_hash=(unsigned char *)Hash160(pubkey,pubkey+pubkey_size).begin(); + + unsigned char pubkey_hash[20]; + uint160 pkhash=Hash160(pubkey,pubkey+pubkey_size); + memcpy(pubkey_hash,&pkhash,20); + size_t elem_size; const unsigned char *elem; int root_stream_name_size; @@ -271,7 +276,7 @@ int mc_MultichainParams::Build(const unsigned char* pubkey, int pubkey_size) { return err; } - + CalculateHash(hash); diff --git a/src/chainparams/chainparams.cpp b/src/chainparams/chainparams.cpp index dae7d887..e0ca04fd 100644 --- a/src/chainparams/chainparams.cpp +++ b/src/chainparams/chainparams.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "chainparams/chainparams.h" @@ -207,6 +207,7 @@ class CMainParams : public CChainParams { fMineBlocksOnDemand = false; fSkipProofOfWorkCheck = false; fTestnetToBeDeprecatedFieldRPC = false; + fDisallowUnsignedBlockNonce = false; } const Checkpoints::CCheckpointData& Checkpoints() const @@ -552,6 +553,17 @@ class CMultiChainParams : public CMainParams { { fAllowMinDifficultyBlocks = (mc_gState->m_NetworkParams->GetInt64Param("allowmindifficultyblocks") != 0); } + fDisallowUnsignedBlockNonce=false; + if(mc_gState->m_Features->NonceInMinerSignature()) + { + if(nTargetTimespan <= 0) + { + if(mc_gState->m_NetworkParams->GetInt64Param("powminimumbits") <= MAX_NBITS_FOR_SIGNED_NONCE) + { + fDisallowUnsignedBlockNonce=true; + } + } + } } void SetMultiChainParam(const char*param_name,int64_t value) diff --git a/src/chainparams/chainparams.h b/src/chainparams/chainparams.h index bd463178..441dcdf7 100644 --- a/src/chainparams/chainparams.h +++ b/src/chainparams/chainparams.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_CHAINPARAMS_H @@ -69,6 +69,8 @@ class CChainParams bool DefaultCheckMemPool() const { return fDefaultCheckMemPool; } /** Allow mining of a min-difficulty block */ bool AllowMinDifficultyBlocks() const { return fAllowMinDifficultyBlocks; } + /** Disallow block nonce not covered by miner signature */ + bool DisallowUnsignedBlockNonce() const { return fDisallowUnsignedBlockNonce; } /** Skip proof-of-work check: allow mining of any difficulty block */ bool SkipProofOfWorkCheck() const { return fSkipProofOfWorkCheck; } /** Make standard checks */ @@ -119,6 +121,7 @@ class CChainParams bool fMineBlocksOnDemand; bool fSkipProofOfWorkCheck; bool fTestnetToBeDeprecatedFieldRPC; + bool fDisallowUnsignedBlockNonce; }; /** diff --git a/src/chainparams/chainparamsbase.cpp b/src/chainparams/chainparamsbase.cpp index 14f2bb6c..e6c498b0 100644 --- a/src/chainparams/chainparamsbase.cpp +++ b/src/chainparams/chainparamsbase.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "chainparams/chainparamsbase.h" diff --git a/src/chainparams/chainparamsbase.h b/src/chainparams/chainparamsbase.h index 28c26629..0456ba9d 100644 --- a/src/chainparams/chainparamsbase.h +++ b/src/chainparams/chainparamsbase.h @@ -1,6 +1,6 @@ // Copyright (c) 2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_CHAINPARAMSBASE_H diff --git a/src/chainparams/globals.h b/src/chainparams/globals.h index 0e55f380..5287472c 100644 --- a/src/chainparams/globals.h +++ b/src/chainparams/globals.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef GLOBALS_H @@ -23,6 +23,8 @@ int64_t MAX_MONEY = 21000000 * COIN; unsigned int MAX_SCRIPT_ELEMENT_SIZE=520; // script.h int MIN_BLOCKS_BETWEEN_UPGRADES = 100; int MAX_OP_RETURN_SHOWN=16384; +int DEFAULT_ACCEPT_FILTER_TIMEOUT=100; +int DEFAULT_SEND_FILTER_TIMEOUT= 20; int MAX_STREAM_QUERY_ITEMS=5000; int MAX_FORMATTED_DATA_DEPTH=100; unsigned int MAX_OP_RETURN_OP_DROP_COUNT=100000000; @@ -30,6 +32,7 @@ uint32_t JSON_NO_DOUBLE_FORMATTING=0; int JSON_DOUBLE_DECIMAL_DIGITS=-1; int MAX_CHUNK_SIZE = 1048576; int MAX_CHUNK_COUNT = 1024; +int MAX_NBITS_FOR_SIGNED_NONCE = 12; int MCP_MAX_STD_OP_RETURN_COUNT=0; int64_t MCP_INITIAL_BLOCK_REWARD=0; diff --git a/src/chainparams/paramlist.h b/src/chainparams/paramlist.h index 9678e79b..00d259fc 100644 --- a/src/chainparams/paramlist.h +++ b/src/chainparams/paramlist.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAINPARAMLIST_H @@ -118,8 +118,12 @@ static const mc_OneMultichainParam MultichainParamArray[] = "Miners must wait * between blocks."}, { "adminconsensusupgrade" , "admin-consensus-upgrade" , MC_PRM_UINT32 | MC_PRM_USER | MC_PRM_CLONE | MC_PRM_DECIMAL , -1, 500000, 0, 1000000, 0.0, 10008, 0, "-mc-adminconsensusupgrade", - "adminconsensusadmin","", + "adminconsensustxfilter","", "* needed to upgrade the chain."}, + { "adminconsensustxfilter" , "admin-consensus-txfilter" , + MC_PRM_UINT32 | MC_PRM_USER | MC_PRM_CLONE | MC_PRM_DECIMAL , -1, 500000, 0, 1000000, 0.0, 20004, 0, "-mc-adminconsensustxfilter", + "adminconsensusadmin","", + "* needed to approve filter in the chain."}, { "adminconsensusadmin" , "admin-consensus-admin" , MC_PRM_UINT32 | MC_PRM_USER | MC_PRM_CLONE | MC_PRM_DECIMAL , -1, 500000, 0, 1000000, 0.0, 10001, 0, "-mc-adminconsensusadmin", "adminconsensusactivate","", diff --git a/src/chainparams/params.cpp b/src/chainparams/params.cpp index 52d5b522..f162ab47 100644 --- a/src/chainparams/params.cpp +++ b/src/chainparams/params.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "multichain/multichain.h" @@ -165,6 +165,11 @@ void* mc_MultichainParams::GetParam(const char *param,int* size) int mc_MultichainParams::IsParamUpgradeValueInRange(const mc_OneMultichainParam *param,int version,int64_t value) { + if((param->m_Type & MC_PRM_DATA_TYPE_MASK) == MC_PRM_BOOLEAN) + { + return 1; + } + if(value >= param->m_MinIntegerValue) { if(value <= param->m_MaxIntegerValue) @@ -280,6 +285,20 @@ int mc_MultichainParams::CanBeUpgradedByVersion(const char *param,int version,in } } + if( (strcmp(param,"anyonecanconnect") == 0) || + (strcmp(param,"anyonecansend") == 0) || + (strcmp(param,"anyonecanreceive") == 0) || + (strcmp(param,"anyonecanreceiveempty") == 0) || + (strcmp(param,"anyonecancreate") == 0) || + (strcmp(param,"anyonecanissue") == 0) || + (strcmp(param,"anyonecanactivate") == 0) ) + { + if(version >= 20007) + { + return m_lpCoord[2 * index + 1]; + } + } + return 0; @@ -1762,11 +1781,6 @@ int mc_MultichainParams::IsProtocolMultichain() } -int mc_Features::MinProtocolVersion() -{ - return 10004; -} - int mc_Features::LastVersionNotSendingProtocolVersionInHandShake() { return 10002; @@ -2037,3 +2051,226 @@ int mc_Features::FixedIn1001120003() return ret; } +int mc_Features::Filters() +{ + int ret=0; + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + int protocol=mc_gState->m_NetworkParams->ProtocolVersion(); + + if(protocol) + { + if(protocol >= 20004) + { + ret=1; + } + } + + return ret; +} + +int mc_Features::CustomPermissions() +{ + int ret=0; + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + int protocol=mc_gState->m_NetworkParams->ProtocolVersion(); + + if(protocol) + { + if(protocol >= 20004) + { + ret=1; + } + } + + return ret; +} + +int mc_Features::StreamFilters() +{ + int ret=0; + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + int protocol=mc_gState->m_NetworkParams->ProtocolVersion(); + + if(protocol) + { + if(protocol >= 20005) + { + ret=1; + } + } + + return ret; +} + +int mc_Features::FixedIn20005() +{ + int ret=0; + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + int protocol=mc_gState->m_NetworkParams->ProtocolVersion(); + + if(protocol) + { + if(protocol >= 20005) + { + ret=1; + } + } + + return ret; +} + +int mc_Features::FilterLimitedMathSet() +{ + int ret=0; + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + int protocol=mc_gState->m_NetworkParams->ProtocolVersion(); + + if(protocol) + { + if(protocol >= 20005) + { + ret=1; + } + } + + return ret; +} + +int mc_Features::FixedIn20006() +{ + int ret=0; + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + int protocol=mc_gState->m_NetworkParams->ProtocolVersion(); + + if(protocol) + { + if(protocol >= 20006) + { + ret=1; + } + } + + return ret; +} + +int mc_Features::NonceInMinerSignature() +{ + int ret=0; + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + int protocol=mc_gState->m_NetworkParams->ProtocolVersion(); + + if(protocol) + { + if(protocol >= 20006) + { + ret=1; + } + } + + return ret; +} + +int mc_Features::ImplicitConnectPermission() +{ + int ret=0; + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + int protocol=mc_gState->m_NetworkParams->ProtocolVersion(); + + if(protocol) + { + if(protocol >= 20006) + { + ret=1; + } + else + { + if(Filters() == 0) + { + ret=1; + } + } + } + + return ret; +} + +int mc_Features::LicenseTokens() +{ + int ret=0; + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + int protocol=mc_gState->m_NetworkParams->ProtocolVersion(); + + if(protocol) + { + if(protocol >= 20007) + { + ret=1; + } + else + { + if(Filters() == 0) + { + ret=1; + } + } + } + + return ret; +} + +int mc_Features::FixedJSDateFunctions() +{ + int ret=0; + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + int protocol=mc_gState->m_NetworkParams->ProtocolVersion(); + + if(protocol) + { + if(protocol >= 20008) + { + ret=1; + } + else + { + if(Filters() == 0) + { + ret=1; + } + } + } + + return ret; +} + + + diff --git a/src/chainparams/params.h b/src/chainparams/params.h index ba1390c5..ba52bc44 100644 --- a/src/chainparams/params.h +++ b/src/chainparams/params.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAINPARAMS_H @@ -68,6 +68,7 @@ extern int64_t MCP_FIRST_BLOCK_REWARD; extern int MCP_TARGET_BLOCK_TIME; extern int MAX_CHUNK_SIZE; extern int MAX_CHUNK_COUNT; +extern int MAX_NBITS_FOR_SIGNED_NONCE; extern int MCP_ANYONE_CAN_ADMIN; extern int MCP_ANYONE_CAN_MINE; extern int MCP_ANYONE_CAN_CONNECT; diff --git a/src/chainparams/state.h b/src/chainparams/state.h index 7ba26417..a7a806cc 100644 --- a/src/chainparams/state.h +++ b/src/chainparams/state.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAIN_STATE_H @@ -41,6 +41,7 @@ #define MC_WMD_NONE 0x00000000 #define MC_WMD_TXS 0x00000001 #define MC_WMD_ADDRESS_TXS 0x00000002 +#define MC_WMD_FLAT_DAT_FILE 0x00000100 #define MC_WMD_MAP_TXS 0x00010000 #define MC_WMD_MODE_MASK 0x00FFFFFF #define MC_WMD_DEBUG 0x01000000 @@ -64,6 +65,7 @@ typedef struct mc_Params char** m_Arguments; int m_FirstArgumentType; char m_DataDirNetSpecific[MC_DCT_DB_MAX_PATH]; + char m_LogDirNetSpecific[MC_DCT_DB_MAX_PATH]; char m_DataDir[MC_DCT_DB_MAX_PATH]; mc_Params() @@ -130,7 +132,6 @@ typedef struct mc_UpgradedParameter typedef struct mc_Features { - int MinProtocolVersion(); int LastVersionNotSendingProtocolVersionInHandShake(); int AnyoneCanReceiveEmpty(); int FormattedData(); @@ -144,6 +145,16 @@ typedef struct mc_Features int Chunks(); int FixedIn1001020003(); int FixedIn1001120003(); + int Filters(); + int CustomPermissions(); + int StreamFilters(); + int FixedIn20005(); + int FilterLimitedMathSet(); + int FixedIn20006(); + int NonceInMinerSignature(); + int ImplicitConnectPermission(); + int LicenseTokens(); + int FixedJSDateFunctions(); } mc_Features; typedef struct mc_BlockHeaderInfo diff --git a/src/checkqueue.h b/src/checkqueue.h index 72b8be1c..96c5d56c 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -1,6 +1,6 @@ // Copyright (c) 2012-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_CHECKQUEUE_H diff --git a/src/community/community.cpp b/src/community/community.cpp new file mode 100644 index 00000000..d07be507 --- /dev/null +++ b/src/community/community.cpp @@ -0,0 +1,149 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "community/community.h" + +using namespace std; + +void mc_EnterpriseFeatures::Zero() +{ + m_Impl=NULL; +} + +void mc_EnterpriseFeatures::Destroy() +{ + Zero(); +} + +int mc_EnterpriseFeatures::Initialize(const char *name,uint32_t mode) +{ + + return MC_ERR_NOERROR; +} + +int mc_EnterpriseFeatures::STR_CreateSubscription(mc_TxEntity *entity,const std::string parameters) +{ + return MC_ERR_FOUND; +} + +int mc_EnterpriseFeatures::STR_IsIndexSkipped(mc_TxImport *import,mc_TxEntity *parent_entity,mc_TxEntity *entity) +{ + return 0; +} + +int mc_EnterpriseFeatures::STR_IsOutOfSync(mc_TxEntity *entity) +{ + return 0; +} + +int mc_EnterpriseFeatures::STR_SetSyncFlag(mc_TxEntity *entity,bool confirm) +{ + return MC_ERR_NOERROR; +} + +int mc_EnterpriseFeatures::STR_GetSubscriptions(mc_Buffer *subscriptions) +{ + return MC_ERR_NOERROR; +} + +int mc_EnterpriseFeatures::STR_PutSubscriptions(mc_Buffer *subscriptions) +{ + return MC_ERR_NOERROR; +} + +int mc_EnterpriseFeatures::WLT_CreateSubscription(mc_TxEntity *entity,uint32_t retrieve,uint32_t indexes,uint32_t *rescan_mode) +{ + *rescan_mode=0; + return MC_ERR_NOERROR; +} + +int mc_EnterpriseFeatures::WLT_DeleteSubscription(mc_TxEntity *entity,uint32_t rescan_mode) +{ + return MC_ERR_NOERROR; +} + +int mc_EnterpriseFeatures::WLT_StartImport() +{ + return MC_ERR_NOERROR; +} + +int mc_EnterpriseFeatures::WLT_CompleteImport() +{ + return MC_ERR_NOERROR; +} + +int mc_EnterpriseFeatures::WLT_NoIndex(mc_TxEntity *entity) +{ + return 0; +} + +int mc_EnterpriseFeatures::WLT_NoRetrieve(mc_TxEntity *entity) +{ + return 0; +} + +string mc_EnterpriseFeatures::ENT_Edition() +{ + return "Community"; +} + +int mc_EnterpriseFeatures::ENT_EditionNumeric() +{ + return 0; +} + +int mc_EnterpriseFeatures::ENT_MinWalletDatVersion() +{ + return 2; +} + +void mc_EnterpriseFeatures::ENT_RPCVerifyEdition() +{ + throw JSONRPCError(RPC_NOT_SUPPORTED, "This feature is available only in Enterprise edition of MultiChain, please call \"help enterprise\" for details"); +} + +std::string mc_EnterpriseFeatures::ENT_TextConstant(const char* name) +{ + return ""; +} + +void mc_EnterpriseFeatures::LIC_RPCVerifyFeature(uint64_t feature) +{ + throw JSONRPCError(RPC_NOT_SUPPORTED, "This feature is available only in Enterprise edition of MultiChain, please call \"help enterprise\" for details"); +} + +bool mc_EnterpriseFeatures::LIC_VerifyFeature(uint64_t feature,std::string& reason) +{ + reason="Not available in Community efition"; + return false; +} + +int mc_EnterpriseFeatures::LIC_VerifyLicenses() +{ + return MC_ERR_NOERROR; +} + +Value mc_EnterpriseFeatures::LIC_RPCDecodeLicenseRequest(const Array& params) +{ + return Value::null; +} + +Value mc_EnterpriseFeatures::LIC_RPCActivateLicense(const Array& params) +{ + return Value::null; +} + +Value mc_EnterpriseFeatures::LIC_RPCTransferLicense(const Array& params) +{ + return Value::null; +} + +Value mc_EnterpriseFeatures::LIC_RPCListLicenseRequests(const Array& params) +{ + return Value::null; +} + +Value mc_EnterpriseFeatures::LIC_RPCGetLicenseConfirmation(const Array& params) +{ + return Value::null; +} diff --git a/src/community/community.h b/src/community/community.h new file mode 100644 index 00000000..7c6aab8b --- /dev/null +++ b/src/community/community.h @@ -0,0 +1,74 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef MULTICHAIN_COMMUNITY_H +#define MULTICHAIN_COMMUNITY_H + +#include "multichain/multichain.h" +#include "wallet/wallettxs.h" +#include "rpc/rpcutils.h" + +#define MC_EFT_NONE 0x0000000000000000 +#define MC_EFT_LICENSE_TRANSFER 0x0000000000000002 +#define MC_EFT_STREAM_CONDITIONAL_INDEXING 0x0000000000000100 +#define MC_EFT_STREAM_MANUAL_RETRIEVAL 0x0000000000000200 +#define MC_EFT_STREAM_READ_PERMISSIONS 0x0000000000001000 +#define MC_EFT_ALL 0xFFFFFFFFFFFFFFFF + + +typedef struct mc_EnterpriseFeatures +{ + void * m_Impl; + + mc_EnterpriseFeatures() + { + Zero(); + } + + ~mc_EnterpriseFeatures() + { + Destroy(); + } + + void Zero(); + void Destroy(); + int Initialize( + const char *name, // Chain name + uint32_t mode); // Unused + + int STR_CreateSubscription(mc_TxEntity *entity,const std::string parameters); + int STR_IsIndexSkipped(mc_TxImport *import,mc_TxEntity *parent_entity,mc_TxEntity *entity); + int STR_IsOutOfSync(mc_TxEntity *entity); + int STR_SetSyncFlag(mc_TxEntity *entity,bool confirm); + int STR_GetSubscriptions(mc_Buffer *subscriptions); + int STR_PutSubscriptions(mc_Buffer *subscriptions); + + int WLT_CreateSubscription(mc_TxEntity *entity,uint32_t retrieve,uint32_t indexes,uint32_t *rescan_mode); + int WLT_DeleteSubscription(mc_TxEntity *entity,uint32_t rescan_mode); + int WLT_StartImport(); + int WLT_CompleteImport(); + int WLT_NoIndex(mc_TxEntity *entity); + int WLT_NoRetrieve(mc_TxEntity *entity); + + std::string ENT_Edition(); + int ENT_EditionNumeric(); + int ENT_MinWalletDatVersion(); + void ENT_RPCVerifyEdition(); + std::string ENT_TextConstant(const char* name); + + void LIC_RPCVerifyFeature(uint64_t feature); + bool LIC_VerifyFeature(uint64_t feature,std::string& reason); +// bool LIC_VerifyConfirmation(uint160 address,void *confirmation, size_t size,std::string& reason); +// string LIC_LicenseName(void *confirmation, size_t size); + int LIC_VerifyLicenses(); + Value LIC_RPCDecodeLicenseRequest(const Array& params); + Value LIC_RPCActivateLicense(const Array& params); + Value LIC_RPCTransferLicense(const Array& params); + Value LIC_RPCListLicenseRequests(const Array& params); + Value LIC_RPCGetLicenseConfirmation(const Array& params); + + +} mc_EnterpriseFeatures; + +#endif /* MULTICHAIN_COMMUNITY_H */ + diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp index f149a08c..4dc62686 100644 --- a/src/compat/glibc_compat.cpp +++ b/src/compat/glibc_compat.cpp @@ -1,6 +1,8 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp index 607e23b5..1b622574 100644 --- a/src/compat/glibc_sanity.cpp +++ b/src/compat/glibc_sanity.cpp @@ -1,6 +1,8 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" diff --git a/src/compat/glibcxx_compat.cpp b/src/compat/glibcxx_compat.cpp index e0b4ac51..8b4a0dfd 100644 --- a/src/compat/glibcxx_compat.cpp +++ b/src/compat/glibcxx_compat.cpp @@ -1,6 +1,8 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. #include #include diff --git a/src/compat/glibcxx_sanity.cpp b/src/compat/glibcxx_sanity.cpp index aafa4a6a..738960cf 100644 --- a/src/compat/glibcxx_sanity.cpp +++ b/src/compat/glibcxx_sanity.cpp @@ -1,6 +1,8 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. #include #include diff --git a/src/compat/sanity.h b/src/compat/sanity.h index 7f5bc1a4..a1e5f8f7 100644 --- a/src/compat/sanity.h +++ b/src/compat/sanity.h @@ -1,6 +1,8 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_COMPAT_SANITY_H #define BITCOIN_COMPAT_SANITY_H diff --git a/src/compat/strnlen.cpp b/src/compat/strnlen.cpp index 7f3e1598..dbeebadd 100644 --- a/src/compat/strnlen.cpp +++ b/src/compat/strnlen.cpp @@ -1,6 +1,8 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" diff --git a/src/core/init-cold.cpp b/src/core/init-cold.cpp index 14ef8046..7c5ecc96 100644 --- a/src/core/init-cold.cpp +++ b/src/core/init-cold.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #if defined(HAVE_CONFIG_H) @@ -25,7 +25,7 @@ #include "utils/util.h" #include "utils/utilmoneystr.h" #ifdef ENABLE_WALLET -#include "wallet/db.h" +#include "wallet/dbwrap.h" #include "wallet/wallet.h" #include "wallet/walletdb.h" #endif @@ -36,6 +36,7 @@ #include "structs/base58.h" #include "multichain/multichain.h" #include "wallet/wallettxs.h" +#include "filters/filter.h" std::string BurnAddress(const std::vector& vchVersion); void HandleSIGTERM(int); @@ -139,16 +140,16 @@ void Shutdown_Cold() StopRPCThreads(); #ifdef ENABLE_WALLET if (pwalletMain) - bitdb.Flush(false); + bitdbwrap.Flush(false); #endif #ifdef ENABLE_WALLET if (pwalletMain) - bitdb.Flush(true); + bitdbwrap.Flush(true); #endif -#ifndef WIN32 +//#ifndef WIN32 boost::filesystem::remove(GetPidFile()); -#endif +//#endif #ifdef ENABLE_WALLET delete pwalletMain; pwalletMain = NULL; @@ -160,6 +161,24 @@ void Shutdown_Cold() } /* MCHN END */ #endif + if(pRelayManager) + { + delete pRelayManager; + pRelayManager=NULL; + } + + if(pMultiChainFilterEngine) + { + delete pMultiChainFilterEngine; + pMultiChainFilterEngine=NULL; + } + + if(pFilterEngine) + { + delete pFilterEngine; + pFilterEngine=NULL; + } + globalVerifyHandle.reset(); ECC_Stop(); LogPrintf("%s: done\n", __func__); @@ -195,7 +214,7 @@ std::string HelpMessage_Cold() strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + " " + _("on startup") + "\n"; strUsage += " -wallet= " + _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), "wallet.dat") + "\n"; /* MCHN START */ - strUsage += " -walletdbversion=1|2 " + _("Specify wallet version, 1 - not scalable, 2 (default) - scalable") + "\n"; + strUsage += " -walletdbversion=2|3 " + _("Specify wallet version, 2 - Berkeley DB, 3 (default) - proprietary") + "\n"; /* MCHN END */ #endif @@ -207,6 +226,7 @@ std::string HelpMessage_Cold() strUsage += ".\n"; strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n"; + strUsage += " -logdir " + _("Send trace/debug info to specified directory") + "\n"; strUsage += " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n"; strUsage += "\n" + _("RPC server options:") + "\n"; @@ -374,9 +394,9 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) #endif /* MCHN END */ -#ifndef WIN32 - CreatePidFile(GetPidFile(), getpid()); -#endif +//#ifndef WIN32 + CreatePidFile(GetPidFile(), __US_GetPID()); +//#endif if (GetBoolArg("-shrinkdebugfile", !fDebug)) ShrinkDebugFile(); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); @@ -390,7 +410,7 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) /* MCHN END */ LogPrintf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION)); #ifdef ENABLE_WALLET - LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); + WalletDBLogVersionString(); #endif if (!fLogTimestamps) LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); @@ -405,11 +425,72 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) // ********************************************************* Step 5: verify wallet database integrity #ifdef ENABLE_WALLET + int currentwalletdatversion=0; + int64_t wallet_mode=GetArg("-walletdbversion",MC_TDB_WALLET_VERSION); + mc_gState->m_WalletMode=MC_WMD_NONE; + std::vector salvagedData; + bool wallet_upgrade=false; if (!fDisableWallet) { LogPrintf("Using wallet %s\n", strWalletFile); uiInterface.InitMessage(_("Verifying wallet...")); - if (!bitdb.Open(GetDataDir())) + + boost::filesystem::path pathWalletDat=GetDataDir() / strWalletFile; + if (filesystem::exists(pathWalletDat)) + { + currentwalletdatversion=GetWalletDatVersion(pathWalletDat.string()); + boost::filesystem::path pathWallet=GetDataDir() / "wallet"; + + LogPrintf("Wallet file exists. WalletDBVersion: %d.\n", currentwalletdatversion); + if( (currentwalletdatversion == 3) && (GetArg("-walletdbversion",MC_TDB_WALLET_VERSION) != 3) ) + { + return InitError(_("Wallet downgrade is not allowed")); + } + if( (currentwalletdatversion == 2) && (GetArg("-walletdbversion",0) == 3) ) + { + if(!boost::filesystem::exists(pathWallet)) + { + currentwalletdatversion=1; + } + else + { + CDBWrapEnv env2; + if (!env2.Open(GetDataDir())) + { + return InitError(_("Error initializing wallet database environment for upgrade")); + } + bool allOK = env2.Salvage(strWalletFile, false, salvagedData); + if(!allOK) + { + return InitError(_("wallet.dat corrupt, cannot upgrade, you should repair it first.\n Run multichaind normally or with -salvagewallet flag")); + } + + currentwalletdatversion=3; + wallet_upgrade=true; + } + } + } + else + { + currentwalletdatversion=wallet_mode; + LogPrintf("Wallet file doesn't exist. New file will be created with version %d.\n", currentwalletdatversion); + } + switch(currentwalletdatversion) + { + case 3: + mc_gState->m_WalletMode |= MC_WMD_FLAT_DAT_FILE; + break; + case 2: + break; + case 1: + return InitError(strprintf("Wallet version 1 is not supported in this version of MultiChain. " + "To upgrade to version 2, run MultiChain 1.0: \n" + "multichaind %s -walletdbversion=2 -rescan\n",mc_gState->m_NetworkParams->Name())); + default: + return InitError(_("Invalid wallet version, possible values 2, 3.\n")); + } + + if (!bitdbwrap.Open(GetDataDir())) { // try moving the database env out of the way boost::filesystem::path pathDatabase = GetDataDir() / "database"; @@ -422,24 +503,51 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) } // try again - if (!bitdb.Open(GetDataDir())) { + if (!bitdbwrap.Open(GetDataDir())) { // if it still fails, it probably means we can't even create the database env string msg = strprintf(_("Error initializing wallet database environment %s!"), strDataDir); return InitError(msg); } } + + if(wallet_upgrade) + { + LogPrintf("Wallet file will be upgraded to version %d.\n", currentwalletdatversion); + if(!bitdbwrap.Recover(strWalletFile,salvagedData)) + { + return InitError(_("Couldn't upgrade wallet.dat")); + } + } + + if (filesystem::exists(pathWalletDat)) + { + currentwalletdatversion=GetWalletDatVersion(pathWalletDat.string()); + } + else + { + currentwalletdatversion=wallet_mode; + } + if (GetBoolArg("-salvagewallet", false)) { // Recover readable keypairs: - if (!CWalletDB::Recover(bitdb, strWalletFile, true)) +// if (!CWalletDB::Recover(bitdbwrap, strWalletFile, true)) + if(!WalletDBRecover(bitdbwrap,strWalletFile,true)) return false; + sprintf(bufOutput,"\nTo work properly with salvaged addresses, you have to call importaddress API and restart MultiChain with -rescan\n\n"); + bytes_written=write(OutputPipe,bufOutput,strlen(bufOutput)); } if (filesystem::exists(GetDataDir() / strWalletFile)) { - CDBEnv::VerifyResult r = bitdb.Verify(strWalletFile, CWalletDB::Recover); - if (r == CDBEnv::RECOVER_OK) +// CDBConstEnv::VerifyResult r = bitdbwrap.Verify(strWalletFile, CWalletDB::Recover); + CDBConstEnv::VerifyResult r = bitdbwrap.Verify(strWalletFile); + if(r != CDBConstEnv::VERIFY_OK) + { + r=WalletDBRecover(bitdbwrap,strWalletFile) ? CDBConstEnv::RECOVER_OK : CDBConstEnv::RECOVER_FAIL; + } + if (r == CDBConstEnv::RECOVER_OK) { string msg = strprintf(_("Warning: wallet.dat corrupt, data salvaged!" " Original wallet.dat saved as wallet.{timestamp}.bak in %s; if" @@ -447,9 +555,13 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) " restore from a backup."), strDataDir); InitWarning(msg); } - if (r == CDBEnv::RECOVER_FAIL) + if (r == CDBConstEnv::RECOVER_FAIL) return InitError(_("wallet.dat corrupt, salvage failed")); } + else + { + bitdbwrap.SetSeekDBName(strWalletFile); + } } // (!fDisableWallet) /* MCHN START*/ @@ -462,7 +574,7 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) char fileName[MC_DCT_DB_MAX_PATH]; mc_GetFullFileName(mc_gState->m_Params->NetworkName(),"params", ".dat",MC_FOM_RELATIVE_TO_DATADIR,fileName); string seed_error=strprintf("Couldn't retrieve blockchain parameters from the seed node in offline mode.\n" - "The file %s must be copied manually from an existing node.\n", + "The file %s must be copied manually from an existing node into empty blockchain directory.\n", fileName); return InitError(seed_error); } @@ -497,35 +609,30 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) return InitError(_(seed_error.c_str())); } - int64_t wallet_mode=GetArg("-walletdbversion",0); - bool wallet_mode_valid=false; + wallet_mode=GetArg("-walletdbversion",0); if(wallet_mode == 0) { mc_gState->m_WalletMode=MC_WMD_AUTO; - wallet_mode_valid=true; } - if(wallet_mode == MC_TDB_WALLET_VERSION) + if(wallet_mode == 3) + { + mc_gState->m_WalletMode=MC_WMD_TXS | MC_WMD_ADDRESS_TXS | MC_WMD_FLAT_DAT_FILE; + } + if(wallet_mode == 2) { mc_gState->m_WalletMode=MC_WMD_TXS | MC_WMD_ADDRESS_TXS; - wallet_mode_valid=true; } if(wallet_mode == 1) { mc_gState->m_WalletMode=MC_WMD_NONE; - wallet_mode_valid=true; zap_wallet_txs=false; } if(wallet_mode == -1) { mc_gState->m_WalletMode=MC_WMD_TXS | MC_WMD_ADDRESS_TXS | MC_WMD_MAP_TXS; - wallet_mode_valid=true; zap_wallet_txs=false; } - - if(!wallet_mode_valid) - { - return InitError(_("Invalid wallet version, possible values 1 or 2.\n")); - } + vector vSubscribedEntities; if(GetBoolArg("-reindex", false) || GetBoolArg("-rescan", false)) @@ -564,12 +671,11 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) pwalletTxsMain=new mc_WalletTxs; mc_TxEntity entity; boost::filesystem::path pathWallet=GetDataDir() / "wallet"; - if(mc_gState->m_WalletMode == MC_WMD_NONE) { if(boost::filesystem::exists(pathWallet)) { - return InitError(strprintf("Wallet was created in version 2. To switch to version 1, with worse performance and scalability, run: \nmultichaind %s -walletdbversion=1 -rescan\n",mc_gState->m_NetworkParams->Name())); + return InitError(strprintf("Wallet was created in version 2 or higher. To switch to version 1, with worse performance and scalability, run: \nmultichaind %s -walletdbversion=1 -rescan\n",mc_gState->m_NetworkParams->Name())); } } else @@ -584,16 +690,21 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) } if(mc_gState->m_WalletMode != MC_WMD_NONE) { - return InitError(strprintf("Wallet was created in version 1. To switch to version 2, with better performance and scalability, run: \nmultichaind %s -walletdbversion=2 -rescan\n",mc_gState->m_NetworkParams->Name())); + return InitError(strprintf("Wallet was created in version 1. To switch to version %d, with better performance and scalability, run: \nmultichaind %s -walletdbversion=%d -rescan\n", + MC_TDB_WALLET_VERSION,mc_gState->m_NetworkParams->Name(),MC_TDB_WALLET_VERSION)); } } else { new_wallet_txs=true; if(mc_gState->m_WalletMode == MC_WMD_AUTO) - { + { mc_gState->m_WalletMode = MC_WMD_TXS | MC_WMD_ADDRESS_TXS; wallet_mode=MC_TDB_WALLET_VERSION; + if(wallet_mode > 2) + { + mc_gState->m_WalletMode |= MC_WMD_FLAT_DAT_FILE; + } } } } @@ -636,6 +747,17 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) wallet_mode=pwalletTxsMain->m_Database->m_DBStat.m_WalletVersion; } + if( (pwalletTxsMain->m_Database->m_DBStat.m_WalletVersion == 2) && (wallet_mode == 3) ) + { + if(wallet_upgrade) + { + if(pwalletTxsMain->UpdateMode(MC_WMD_FLAT_DAT_FILE)) + { + return InitError(_("Couldn't update wallet mode")); + } + } + } + if((pwalletTxsMain->m_Database->m_DBStat.m_WalletVersion) != wallet_mode) { return InitError(strprintf("Wallet tx database was created with different wallet version (%d). Please restart multichaind with reindex=1 \n",pwalletTxsMain->m_Database->m_DBStat.m_WalletVersion)); @@ -664,7 +786,11 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) if (nLoadWalletRetForBuild != DB_LOAD_OK) // MCHN-TODO wallet recovery { - return InitError(_("wallet.dat corrupted. Please remove it and restart.")); + if (GetBoolArg("-salvagewallet", false)) + { + return InitError(_("wallet.dat corrupted. Please remove it and restart.")); + } + return InitError(_("wallet.dat corrupted. Please try running MultiChain with -salvagewallet.")); } if(!pwalletMain->vchDefaultKey.IsValid()) @@ -828,10 +954,11 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) pwalletMain=NULL; + string rpc_threads_error=""; if (fServer) { uiInterface.InitMessage.connect(SetRPCWarmupStatus); - StartRPCThreads(); + StartRPCThreads(rpc_threads_error); } /* MCHN END*/ @@ -852,6 +979,20 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) */ /* MCHN END */ + std::string strResult; + + pFilterEngine=new mc_FilterEngine(); + if (pFilterEngine->Initialize(strResult) != MC_ERR_NOERROR) + { + return InitError(strprintf(_("Couldn't initialize filter engine: '%s'"), strResult)); + } + + pMultiChainFilterEngine=new mc_MultiChainFilterEngine; + if(pMultiChainFilterEngine->Initialize()) + { + return InitError(_("Couldn't initialize filter engine.")); + } + // ********************************************************* Step 7: load block chain fReindex = GetBoolArg("-reindex", false); @@ -953,7 +1094,8 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) // If the loaded chain has a wrong genesis, bail out immediately // (we're likely using a testnet datadir, or the other way around). if (!mapBlockIndex.empty() && mapBlockIndex.count(Params().HashGenesisBlock()) == 0) - return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); + return InitError(_("This blockchain was created with a different params.dat file, please restore the original.")); +// return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); // Initialize the block index (no-op if non-empty database was already loaded) if (!InitBlockIndex()) { @@ -1021,6 +1163,14 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) bytes_written=write(OutputPipe,bufOutput,strlen(bufOutput)); } } + if(rpc_threads_error.size()) + { + if(!GetBoolArg("-shortoutput", false)) + { + sprintf(bufOutput,"%s\n",rpc_threads_error.c_str()); + bytes_written=write(OutputPipe,bufOutput,strlen(bufOutput)); + } + } int version=mc_gState->m_NetworkParams->ProtocolVersion(); @@ -1233,7 +1383,7 @@ bool AppInit2_Cold(boost::thread_group& threadGroup,int OutputPipe) #endif if (pwalletMain) - bitdb.Flush(false); + bitdbwrap.Flush(false); #ifdef ENABLE_WALLET if (pwalletMain) { diff --git a/src/core/init.cpp b/src/core/init.cpp index 07ea282a..eedef9c5 100644 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #if defined(HAVE_CONFIG_H) @@ -25,10 +25,11 @@ #include "utils/util.h" #include "utils/utilmoneystr.h" #ifdef ENABLE_WALLET -#include "wallet/db.h" +#include "wallet/dbwrap.h" #include "wallet/wallet.h" #include "wallet/walletdb.h" #endif +#include "community/community.h" /* MCHN START */ @@ -37,6 +38,7 @@ #include "multichain/multichain.h" #include "wallet/wallettxs.h" #include "protocol/relay.h" +#include "filters/filter.h" std::string BurnAddress(const std::vector& vchVersion); std::string SetBannedTxs(std::string txlist); @@ -66,6 +68,9 @@ CWallet* pwalletMain = NULL; mc_WalletTxs* pwalletTxsMain = NULL; #endif mc_RelayManager* pRelayManager = NULL; +mc_FilterEngine* pFilterEngine = NULL; +mc_MultiChainFilterEngine* pMultiChainFilterEngine = NULL; + bool fFeeEstimatesInitialized = false; extern int JSON_DOUBLE_DECIMAL_DIGITS; @@ -89,6 +94,11 @@ enum BindFlags { static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; CClientUIInterface uiInterface; +//! -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB +static const CAmount nHighTransactionFeeWarning = 0.01 * COIN; +//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis) +static const CAmount nHighTransactionMaxFeeWarning = 100 * nHighTransactionFeeWarning; + ////////////////////////////////////////////////////////////////////////////// // // Shutdown @@ -172,7 +182,7 @@ void Shutdown() StopRPCThreads(); #ifdef ENABLE_WALLET if (pwalletMain) - bitdb.Flush(false); + bitdbwrap.Flush(false); GenerateBitcoins(false, NULL, 0); #endif StopNode(); @@ -205,16 +215,17 @@ void Shutdown() } #ifdef ENABLE_WALLET if (pwalletMain) - bitdb.Flush(true); + bitdbwrap.Flush(true); #endif -#ifndef WIN32 +//#ifndef WIN32 boost::filesystem::remove(GetPidFile()); -#endif +//#endif UnregisterAllValidationInterfaces(); #ifdef ENABLE_WALLET delete pwalletMain; pwalletMain = NULL; /* MCHN START */ + if(pwalletTxsMain) { delete pwalletTxsMain; @@ -225,6 +236,20 @@ void Shutdown() delete pRelayManager; pRelayManager=NULL; } + + if(pMultiChainFilterEngine) + { + delete pMultiChainFilterEngine; + pMultiChainFilterEngine=NULL; + } + + + if(pFilterEngine) + { + delete pFilterEngine; + pFilterEngine=NULL; + } + /* MCHN END */ #endif globalVerifyHandle.reset(); @@ -356,6 +381,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += " -forcednsseed " + strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), 0) + "\n"; strUsage += " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n"; strUsage += " -maxconnections= " + strprintf(_("Maintain at most connections to peers (default: %u)"), 125) + "\n"; + strUsage += " -maxoutconnections= " + strprintf(_("Open at most outbound connections to peers (1-32, default: %u)"), 8) + "\n"; strUsage += " -maxreceivebuffer= " + strprintf(_("Maximum per-connection receive buffer, *1000 bytes (default: %u)"), 5000) + "\n"; strUsage += " -maxsendbuffer= " + strprintf(_("Maximum per-connection send buffer, *1000 bytes (default: %u)"), 100000) + "\n"; strUsage += " -onion= " + strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy") + "\n"; @@ -397,7 +423,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += " -walletnotifynew= " + _("Execute this command when a transaction is first seen, if it relates to an address in the wallet or a subscribed asset or stream. ") + "\n"; strUsage += " " + _("(more details and % substitutions online)") + "\n"; /* MCHN START */ - strUsage += " -walletdbversion=1|2 " + _("Specify wallet version, 1 - not scalable, 2 (default) - scalable") + "\n"; + strUsage += " -walletdbversion=2|3 " + _("Specify wallet version, 2 - Berkeley DB, 3 (default) - proprietary") + "\n"; strUsage += " -autosubscribe=streams|assets|\"streams,assets\"|\"assets,streams\" " + _("Automatically subscribe to new streams and/or assets") + "\n"; /* MCHN END */ strUsage += " -zapwallettxes= " + _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + "\n"; @@ -438,6 +464,7 @@ std::string HelpMessage(HelpMessageMode mode) } strUsage += " -minrelaytxfee= " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for relaying (default: %s)"), FormatMoney(::minRelayTxFee.GetFeePerK())) + "\n"; strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n"; + strUsage += " -logdir " + _("Send trace/debug info to specified directory") + "\n"; if (GetBoolArg("-help-debug", false)) { strUsage += " -printpriority " + strprintf(_("Log transaction priority and fee per kB when mining blocks (default: %u)"), 0) + "\n"; @@ -500,6 +527,9 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += " -chunkquerytimeout= " + _("Timeout, after which undelivered chunk is moved to the end of the chunk queue, default 25s") + "\n"; strUsage += " -chunkrequesttimeout= " + _("Timeout, after which chunk request is dropped and another source is tried, default 10s") + "\n"; strUsage += " -flushsourcechunks= " + _("Flush offchain items created by this node to disk immediately when created, default 1") + "\n"; + strUsage += " -acceptfiltertimeout= " + strprintf(_("Timeout, after which filter execution will be aborted, when accepting new txs, in milliseconds, default %u"),DEFAULT_ACCEPT_FILTER_TIMEOUT) + "\n"; + strUsage += " -sendfiltertimeout= " + strprintf(_("Timeout, after which filter execution will be aborted, when tx is sent from this node, in milliseconds, default %u"),DEFAULT_SEND_FILTER_TIMEOUT) + "\n"; + strUsage += " -lockinlinemetadata= " + _("Outputs with inline metadata can be sent only using create/appendrawtransaction, default 1") + "\n"; strUsage += "\n" + _("MultiChain API response parameters") + "\n"; strUsage += " -hideknownopdrops= " + strprintf(_("Remove recognized MultiChain OP_DROP metadata from the responses to JSON-RPC calls (default: %u)"), 0) + "\n"; @@ -647,7 +677,7 @@ bool InitSanityCheck(void) */ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) { -/* MCHN START */ +/* MCHN START */ char bufOutput[4096]; size_t bytes_written; /* MCHN END */ @@ -808,7 +838,14 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) // Check for -tor - as this is a privacy risk to continue, exit here if (GetBoolArg("-tor", false)) return InitError(_("Error: Unsupported argument -tor found, use -onion.")); - + int MaxOutConnections=GetArg("-maxoutconnections",8); + if ( (MaxOutConnections < 1) || (MaxOutConnections > 32) ) + { + return InitError(_("Error: -maxoutconnections out of range.")); + } + + + if (GetBoolArg("-benchmark", false)) InitWarning(_("Warning: Unsupported argument -benchmark ignored, use -debug=bench.")); @@ -861,42 +898,6 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) } #ifdef ENABLE_WALLET - if (mapArgs.count("-mintxfee")) - { - CAmount n = 0; - if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0) - CWallet::minTxFee = CFeeRate(n); - else - return InitError(strprintf(_("Invalid amount for -mintxfee=: '%s'"), mapArgs["-mintxfee"])); - } - if (mapArgs.count("-paytxfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(mapArgs["-paytxfee"], nFeePerK)) - return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s'"), mapArgs["-paytxfee"])); - if (nFeePerK > nHighTransactionFeeWarning) - InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); - payTxFee = CFeeRate(nFeePerK, 1000); - if (payTxFee < ::minRelayTxFee) - { - return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), - mapArgs["-paytxfee"], ::minRelayTxFee.ToString())); - } - } - if (mapArgs.count("-maxtxfee")) - { - CAmount nMaxFee = 0; - if (!ParseMoney(mapArgs["-maxtxfee"], nMaxFee)) - return InitError(strprintf(_("Invalid amount for -maxtxfee=: '%s'"), mapArgs["-maptxfee"])); - if (nMaxFee > nHighTransactionMaxFeeWarning) - InitWarning(_("Warning: -maxtxfee is set very high! Fees this large could be paid on a single transaction.")); - maxTxFee = nMaxFee; - if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) - { - return InitError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), - mapArgs["-maxtxfee"], ::minRelayTxFee.ToString())); - } - } nTxConfirmTarget = GetArg("-txconfirmtarget", 1); bSpendZeroConfChange = GetArg("-spendzeroconfchange", true); fSendFreeTransactions = GetArg("-sendfreetransactions", false); @@ -939,9 +940,9 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) #endif /* MCHN END */ -#ifndef WIN32 - CreatePidFile(GetPidFile(), getpid()); -#endif +//#ifndef WIN32 + CreatePidFile(GetPidFile(), __US_GetPID()); +//#endif if (GetBoolArg("-shrinkdebugfile", !fDebug)) ShrinkDebugFile(); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); @@ -955,7 +956,7 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) /* MCHN END */ LogPrintf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION)); #ifdef ENABLE_WALLET - LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); + WalletDBLogVersionString(); #endif if (!fLogTimestamps) LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); @@ -991,14 +992,91 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) } */ int64_t nStart; - // ********************************************************* Step 5: verify wallet database integrity #ifdef ENABLE_WALLET + int currentwalletdatversion=0; + int64_t wallet_mode=GetArg("-walletdbversion",MC_TDB_WALLET_VERSION); + mc_gState->m_WalletMode=MC_WMD_NONE; + std::vector salvagedData; + bool wallet_upgrade=false; + if (!fDisableWallet) { LogPrintf("Using wallet %s\n", strWalletFile); uiInterface.InitMessage(_("Verifying wallet...")); - if (!bitdb.Open(GetDataDir())) + + boost::filesystem::path pathWalletDat=GetDataDir() / strWalletFile; + if (filesystem::exists(pathWalletDat)) + { + currentwalletdatversion=GetWalletDatVersion(pathWalletDat.string()); + boost::filesystem::path pathWallet=GetDataDir() / "wallet"; + + LogPrintf("Wallet file exists. WalletDBVersion: %d.\n", currentwalletdatversion); + if(currentwalletdatversion != 1) + { + if(pEF->ENT_MinWalletDatVersion() > currentwalletdatversion) + { + return InitError(strprintf("Wallet version %d is not supported in this edition of MultiChain.\n" + "To upgrade to version %d, run MultiChain Offline Daemon: \n" + "multichaind-cold %s -datadir=%s -walletdbversion=3\n" + "and restart multichaind or use Community Edition.\n", + currentwalletdatversion,pEF->ENT_MinWalletDatVersion(), mc_gState->m_NetworkParams->Name(),mc_gState->m_Params->DataDir(0,0))); + } + } + if( (currentwalletdatversion == 3) && (GetArg("-walletdbversion",MC_TDB_WALLET_VERSION) != 3) ) + { + return InitError(_("Wallet downgrade is not allowed")); + } + if( (currentwalletdatversion == 2) && (GetArg("-walletdbversion",0) == 3) ) + { + if(!boost::filesystem::exists(pathWallet)) + { + currentwalletdatversion=1; + } + else + { + CDBWrapEnv env2; + if (!env2.Open(GetDataDir())) + { + return InitError(_("Error initializing wallet database environment for upgrade")); + } + bool allOK = env2.Salvage(strWalletFile, false, salvagedData); + if(!allOK) + { + return InitError(_("wallet.dat corrupt, cannot upgrade, you should repair it first.\n Run multichaind normally or with -salvagewallet flag")); + } + + currentwalletdatversion=3; + wallet_upgrade=true; + } + } + } + else + { + currentwalletdatversion=wallet_mode; + if(pEF->ENT_MinWalletDatVersion() > currentwalletdatversion) + { + return InitError(strprintf("Wallet version %d is not supported in this edition of MultiChain.\n",currentwalletdatversion)); + } + + LogPrintf("Wallet file doesn't exist. New file will be created with version %d.\n", currentwalletdatversion); + } + switch(currentwalletdatversion) + { + case 3: + mc_gState->m_WalletMode |= MC_WMD_FLAT_DAT_FILE; + break; + case 2: + break; + case 1: + return InitError(strprintf("Wallet version 1 is not supported in this version of MultiChain. " + "To upgrade to version 2, run MultiChain 1.0: \n" + "multichaind %s -walletdbversion=2 -rescan\n",mc_gState->m_NetworkParams->Name())); + default: + return InitError(_("Invalid wallet version, possible values 2, 3.\n")); + } + + if (!bitdbwrap.Open(GetDataDir())) { // try moving the database env out of the way boost::filesystem::path pathDatabase = GetDataDir() / "database"; @@ -1011,24 +1089,52 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) } // try again - if (!bitdb.Open(GetDataDir())) { + if (!bitdbwrap.Open(GetDataDir())) { // if it still fails, it probably means we can't even create the database env string msg = strprintf(_("Error initializing wallet database environment %s!"), strDataDir); return InitError(msg); } + } + + if(wallet_upgrade) + { + LogPrintf("Wallet file will be upgraded to version %d.\n", currentwalletdatversion); + if(!bitdbwrap.Recover(strWalletFile,salvagedData)) + { + return InitError(_("Couldn't upgrade wallet.dat")); + } + } + + if (filesystem::exists(pathWalletDat)) + { + currentwalletdatversion=GetWalletDatVersion(pathWalletDat.string()); + } + else + { + currentwalletdatversion=wallet_mode; + } + if (GetBoolArg("-salvagewallet", false)) { // Recover readable keypairs: - if (!CWalletDB::Recover(bitdb, strWalletFile, true)) +// if (!CWalletDB::Recover(bitdbwrap, strWalletFile, true)) + if(!WalletDBRecover(bitdbwrap,strWalletFile,true)) return false; + sprintf(bufOutput,"\nTo work properly with salvaged addresses, you have to call importaddress API and restart MultiChain with -rescan\n\n"); + bytes_written=write(OutputPipe,bufOutput,strlen(bufOutput)); } if (filesystem::exists(GetDataDir() / strWalletFile)) { - CDBEnv::VerifyResult r = bitdb.Verify(strWalletFile, CWalletDB::Recover); - if (r == CDBEnv::RECOVER_OK) +// CDBConstEnv::VerifyResult r = bitdbwrap.Verify(strWalletFile, CWalletDB::Recover); + CDBConstEnv::VerifyResult r = bitdbwrap.Verify(strWalletFile); + if(r != CDBConstEnv::VERIFY_OK) + { + r=WalletDBRecover(bitdbwrap,strWalletFile) ? CDBConstEnv::RECOVER_OK : CDBConstEnv::RECOVER_FAIL; + } + if (r == CDBConstEnv::RECOVER_OK) { string msg = strprintf(_("Warning: wallet.dat corrupt, data salvaged!" " Original wallet.dat saved as wallet.{timestamp}.bak in %s; if" @@ -1036,9 +1142,14 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) " restore from a backup."), strDataDir); InitWarning(msg); } - if (r == CDBEnv::RECOVER_FAIL) + if (r == CDBConstEnv::RECOVER_FAIL) return InitError(_("wallet.dat corrupt, salvage failed")); } + else + { + bitdbwrap.SetSeekDBName(strWalletFile); + } + } // (!fDisableWallet) /* MCHN START*/ @@ -1077,7 +1188,11 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) if (nLoadWalletRetForBuild != DB_LOAD_OK) // MCHN-TODO wallet recovery { - return InitError(_("wallet.dat corrupted. Please remove it and restart.")); + if (GetBoolArg("-salvagewallet", false)) + { + return InitError(_("wallet.dat corrupted. Please remove it and restart.")); + } + return InitError(_("wallet.dat is partially corrupted. Please try running MultiChain with -salvagewallet.")); } if(!pwalletMain->vchDefaultKey.IsValid()) @@ -1091,7 +1206,6 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) LogPrintf("mchn: Default key is not found - creating new... \n"); // Create new keyUser and set as default key // RandAddSeedPerfmon(); - pwalletMain->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately CPubKey newDefaultKey; if (pwalletMain->GetKeyFromPool(newDefaultKey)) { @@ -1121,6 +1235,7 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) bool zap_wallet_txs=false; bool new_wallet_txs=false; seed_node=mc_gState->GetSeedNode(); + mc_Buffer *rescan_subscriptions=NULL; int seed_attempt=1; if(init_privkey.size()) @@ -1356,36 +1471,31 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) string strBurnAddress=BurnAddress(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)); // Caching burn address LogPrint("mchn","mchn: Burn address: %s\n",strBurnAddress.c_str()); - int64_t wallet_mode=GetArg("-walletdbversion",0); - bool wallet_mode_valid=false; + wallet_mode=GetArg("-walletdbversion",0); if(wallet_mode == 0) { mc_gState->m_WalletMode=MC_WMD_AUTO; - wallet_mode_valid=true; } - if(wallet_mode == MC_TDB_WALLET_VERSION) + if(wallet_mode == 3) + { + mc_gState->m_WalletMode=MC_WMD_TXS | MC_WMD_ADDRESS_TXS | MC_WMD_FLAT_DAT_FILE; + } + if(wallet_mode == 2) { mc_gState->m_WalletMode=MC_WMD_TXS | MC_WMD_ADDRESS_TXS; - wallet_mode_valid=true; } + if(wallet_mode == 1) { mc_gState->m_WalletMode=MC_WMD_NONE; - wallet_mode_valid=true; zap_wallet_txs=false; } if(wallet_mode == -1) { mc_gState->m_WalletMode=MC_WMD_TXS | MC_WMD_ADDRESS_TXS | MC_WMD_MAP_TXS; - wallet_mode_valid=true; zap_wallet_txs=false; } - if(!wallet_mode_valid) - { - return InitError(_("Invalid wallet version, possible values 1 or 2.\n")); - } - vector vSubscribedEntities; if(GetBoolArg("-reindex", false) || GetBoolArg("-rescan", false)) { @@ -1411,6 +1521,16 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) } } __US_Sleep(1000); + rescan_subscriptions=new mc_Buffer; + mc_EnterpriseFeatures* pRescanEF = new mc_EnterpriseFeatures; + if(pRescanEF->Initialize(mc_gState->m_Params->NetworkName(),0)) + { + fprintf(stderr,"\nError: Cannot intitialize Enterprise features. Exiting...\n\n"); + return false; + } + pRescanEF->STR_GetSubscriptions(rescan_subscriptions); + pRescanEF->Destroy(); + delete pRescanEF; } pwalletTxsMain->Destroy(); delete pwalletTxsMain; @@ -1424,12 +1544,12 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) pwalletTxsMain=new mc_WalletTxs; mc_TxEntity entity; boost::filesystem::path pathWallet=GetDataDir() / "wallet"; - + if(mc_gState->m_WalletMode == MC_WMD_NONE) { if(boost::filesystem::exists(pathWallet)) { - return InitError(strprintf("Wallet was created in version 2. To switch to version 1, with worse performance and scalability, run: \nmultichaind %s -walletdbversion=1 -rescan\n",mc_gState->m_NetworkParams->Name())); + return InitError(strprintf("Wallet was created in version 2 or higher. To switch to version 1, with worse performance and scalability, run: \nmultichaind %s -walletdbversion=1 -rescan\n",mc_gState->m_NetworkParams->Name())); } } else @@ -1444,16 +1564,21 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) } if(mc_gState->m_WalletMode != MC_WMD_NONE) { - return InitError(strprintf("Wallet was created in version 1. To switch to version 2, with better performance and scalability, run: \nmultichaind %s -walletdbversion=2 -rescan\n",mc_gState->m_NetworkParams->Name())); + return InitError(strprintf("Wallet was created in version 1. To switch to version %d, with better performance and scalability, run: \nmultichaind %s -walletdbversion=%d -rescan\n", + MC_TDB_WALLET_VERSION,mc_gState->m_NetworkParams->Name(),MC_TDB_WALLET_VERSION)); } } else { new_wallet_txs=true; if(mc_gState->m_WalletMode == MC_WMD_AUTO) - { + { mc_gState->m_WalletMode = MC_WMD_TXS | MC_WMD_ADDRESS_TXS; wallet_mode=MC_TDB_WALLET_VERSION; + if(wallet_mode > 2) + { + mc_gState->m_WalletMode |= MC_WMD_FLAT_DAT_FILE; + } } } } @@ -1500,6 +1625,17 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) wallet_mode=pwalletTxsMain->m_Database->m_DBStat.m_WalletVersion; } + if( (pwalletTxsMain->m_Database->m_DBStat.m_WalletVersion == 2) && (wallet_mode == 3) ) + { + if(wallet_upgrade) + { + if(pwalletTxsMain->UpdateMode(MC_WMD_FLAT_DAT_FILE)) + { + return InitError(_("Couldn't update wallet mode")); + } + } + } + if((pwalletTxsMain->m_Database->m_DBStat.m_WalletVersion) != wallet_mode) { return InitError(strprintf("Wallet tx database was created with different wallet version (%d). Please restart multichaind with reindex=1 \n",pwalletTxsMain->m_Database->m_DBStat.m_WalletVersion)); @@ -1528,7 +1664,11 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) if (nLoadWalletRetForBuild != DB_LOAD_OK) // MCHN-TODO wallet recovery { - return InitError(_("wallet.dat corrupted. Please remove it and restart.")); + if (GetBoolArg("-salvagewallet", false)) + { + return InitError(_("wallet.dat corrupted. Please remove it and restart.")); + } + return InitError(_("wallet.dat corrupted. Please try running MultiChain with -salvagewallet.")); } if(!pwalletMain->vchDefaultKey.IsValid()) @@ -1763,15 +1903,53 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) pwalletMain=NULL; + string rpc_threads_error=""; if (fServer) { JSON_DOUBLE_DECIMAL_DIGITS=GetArg("-apidecimaldigits",-1); uiInterface.InitMessage.connect(SetRPCWarmupStatus); - StartRPCThreads(); + StartRPCThreads(rpc_threads_error); } /* MCHN END*/ - + ::minRelayTxFee = CFeeRate(MIN_RELAY_TX_FEE); + if (mapArgs.count("-mintxfee")) + { + CAmount n = 0; + if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0) + CWallet::minTxFee = CFeeRate(n); + else + return InitError(strprintf(_("Invalid amount for -mintxfee=: '%s'"), mapArgs["-mintxfee"])); + } + if (mapArgs.count("-paytxfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(mapArgs["-paytxfee"], nFeePerK)) + return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s'"), mapArgs["-paytxfee"])); + if (nFeePerK > nHighTransactionFeeWarning) + InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); + payTxFee = CFeeRate(nFeePerK, 1000); + if (payTxFee < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), + mapArgs["-paytxfee"], ::minRelayTxFee.ToString())); + } + } + if (mapArgs.count("-maxtxfee")) + { + CAmount nMaxFee = 0; + if (!ParseMoney(mapArgs["-maxtxfee"], nMaxFee)) + return InitError(strprintf(_("Invalid amount for -maxtxfee=: '%s'"), mapArgs["-maptxfee"])); + if (nMaxFee > nHighTransactionMaxFeeWarning) + InitWarning(_("Warning: -maxtxfee is set very high! Fees this large could be paid on a single transaction.")); + maxTxFee = nMaxFee; + if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), + mapArgs["-maxtxfee"], ::minRelayTxFee.ToString())); + } + } + #endif // ENABLE_WALLET // ********************************************************* Step 6: network initialization @@ -1873,6 +2051,20 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); } /* MCHN START */ + std::string strResult; + pFilterEngine=new mc_FilterEngine(); + if (pFilterEngine->Initialize(strResult) != MC_ERR_NOERROR) + { + return InitError(strprintf(_("Couldn't initialize filter engine: '%s'"), strResult)); + } + + pMultiChainFilterEngine=new mc_MultiChainFilterEngine; + if(pMultiChainFilterEngine->Initialize()) + { + return InitError(_("Couldn't initialize filter engine.")); + } + + pRelayManager=new mc_RelayManager; int max_ips=64; @@ -2174,6 +2366,14 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) bytes_written=write(OutputPipe,bufOutput,strlen(bufOutput)); } } + if(rpc_threads_error.size()) + { + if(!GetBoolArg("-shortoutput", false)) + { + sprintf(bufOutput,"%s\n",rpc_threads_error.c_str()); + bytes_written=write(OutputPipe,bufOutput,strlen(bufOutput)); + } + } // int version=mc_gState->m_NetworkParams->GetInt64Param("protocolversion"); int version=mc_gState->m_NetworkParams->ProtocolVersion(); @@ -2196,6 +2396,18 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) } } + + if(pEF->Initialize(mc_gState->m_Params->NetworkName(),0)) + { + fprintf(stderr,"\nError: Cannot intitialize Enterprise features. Exiting...\n\n"); + return false; + } + if(rescan_subscriptions) + { + pEF->STR_PutSubscriptions(rescan_subscriptions); + delete rescan_subscriptions; + } + // ********************************************************* Step 8: load wallet #ifdef ENABLE_WALLET @@ -2394,6 +2606,7 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) return InitError(strErrors.str()); pRelayManager->Initialize(); + // RandAddSeedPerfmon(); @@ -2415,7 +2628,7 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) #endif if (pwalletMain) - bitdb.Flush(false); + bitdbwrap.Flush(false); if(!GetBoolArg("-offline",false)) { diff --git a/src/core/init.h b/src/core/init.h index f0ee4bd6..6458b904 100644 --- a/src/core/init.h +++ b/src/core/init.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_INIT_H @@ -12,6 +12,9 @@ class CWallet; struct mc_WalletTxs; struct mc_RelayManager; +struct mc_FilterEngine; +struct mc_MultiChainFilterEngine; +struct mc_EnterpriseFeatures; namespace boost { @@ -21,6 +24,9 @@ class thread_group; extern CWallet* pwalletMain; extern mc_WalletTxs* pwalletTxsMain; extern mc_RelayManager* pRelayManager; +extern mc_FilterEngine* pFilterEngine; +extern mc_MultiChainFilterEngine* pMultiChainFilterEngine; +extern mc_EnterpriseFeatures* pEF; void StartShutdown(); diff --git a/src/core/main.cpp b/src/core/main.cpp index 175edd5f..cde8c33e 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "core/main.h" @@ -35,6 +35,8 @@ extern mc_WalletTxs* pwalletTxsMain; extern mc_RelayManager* pRelayManager; +extern mc_FilterEngine* pFilterEngine; +extern mc_MultiChainFilterEngine* pMultiChainFilterEngine; /* MCHN END */ @@ -55,12 +57,13 @@ bool AcceptMultiChainTransaction(const CTransaction& tx, string& reason, int64_t *mandatory_fee_out, uint32_t *replay); +bool Is_MultiChainLicenseTokenTransfer(const CTransaction& tx); bool ExtractDestinationScriptValid(const CScript& scriptPubKey, CTxDestination& addressRet); bool AcceptAssetTransfers(const CTransaction& tx, const CCoinsViewCache &inputs, string& reason); bool AcceptAssetGenesis(const CTransaction &tx,int offset,bool accept,string& reason); bool AcceptPermissionsAndCheckForDust(const CTransaction &tx,bool accept,string& reason); bool ReplayMemPool(CTxMemPool& pool, int from,bool accept); -bool VerifyBlockSignature(CBlock *block,bool force); +bool VerifyBlockSignature(CBlock *block,bool force,bool in_sync); bool VerifyBlockMiner(CBlock *block,CBlockIndex* pindexNew); bool CheckBlockPermissions(const CBlock& block,CBlockIndex* prev_block,unsigned char *lpMinerAddress); bool ProcessMultichainRelay(CNode* pfrom, CDataStream& vRecv, CValidationState &state); @@ -437,6 +440,41 @@ int SetUpgradedParamValue(const mc_OneMultichainParam *param,int64_t value) MAX_CHUNK_COUNT=value; } + if(strcmp(param->m_Name,"anyonecanconnect") == 0) + { + MCP_ANYONE_CAN_CONNECT=value; + } + + if(strcmp(param->m_Name,"anyonecansend") == 0) + { + MCP_ANYONE_CAN_SEND=value; + } + + if(strcmp(param->m_Name,"anyonecanreceive") == 0) + { + MCP_ANYONE_CAN_RECEIVE=value; + } + + if(strcmp(param->m_Name,"anyonecanreceiveempty") == 0) + { + MCP_ANYONE_CAN_RECEIVE_EMPTY=value; + } + + if(strcmp(param->m_Name,"anyonecancreate") == 0) + { + MCP_ANYONE_CAN_CREATE=value; + } + + if(strcmp(param->m_Name,"anyonecanissue") == 0) + { + MCP_ANYONE_CAN_ISSUE=value; + } + + if(strcmp(param->m_Name,"anyonecanactivate") == 0) + { + MCP_ANYONE_CAN_ACTIVATE=value; + } + return MC_ERR_NOERROR; } @@ -499,93 +537,6 @@ int MultichainNode_ApplyUpgrades(int current_height) } -int MultichainNode_ApplyUpgrades_Old(int current_height) -{ - mc_EntityDetails entity; - mc_Buffer *permissions; - permissions=NULL; - map map_sorted; - - int OriginalProtocolVersion=(int)mc_gState->m_NetworkParams->GetInt64Param("protocolversion"); - int CurrentProtocolVersion=mc_gState->m_NetworkParams->ProtocolVersion();//mc_gState->m_ProtocolVersionToUpgrade; - int NewProtocolVersion=OriginalProtocolVersion; - int version; - - permissions=mc_gState->m_Permissions->GetUpgradeList(NULL,NULL); - - for(int i=0;iGetCount();i++) - { - mc_PermissionDetails *plsRow; - plsRow=(mc_PermissionDetails *)(permissions->GetRow(i)); - if(plsRow->m_Type == MC_PTP_UPGRADE) - { - map_sorted.insert(std::make_pair(plsRow->m_LastRow,i)); - } - } - - BOOST_FOREACH(PAIRTYPE(const uint64_t, int)& item, map_sorted) -// for(int i=0;iGetCount();i++) - { - int i=item.second; - mc_PermissionDetails *plsRow; - plsRow=(mc_PermissionDetails *)(permissions->GetRow(i)); - if(plsRow->m_Type == MC_PTP_UPGRADE) - { - if(plsRow->m_BlockFrom < plsRow->m_BlockTo) - { - if(mc_gState->m_Assets->FindEntityByShortTxID(&entity,plsRow->m_Address)) - { - int applied_height=entity.UpgradeStartBlock(); - if((int)plsRow->m_BlockReceived > applied_height) - { - applied_height=plsRow->m_BlockReceived; - } - if(current_height >=applied_height) - { - version=entity.UpgradeProtocolVersion(); - if(version >= mc_gState->MinProtocolDowngradeVersion()) - { - if((NewProtocolVersion < mc_gState->MinProtocolForbiddenDowngradeVersion()) || (version >= NewProtocolVersion)) - { - NewProtocolVersion=version; - } - } - } - } - } - } - } - - mc_gState->m_Permissions->FreePermissionList(permissions); - mc_gState->m_ProtocolVersionToUpgrade=NewProtocolVersion; - - if(mc_gState->m_ProtocolVersionToUpgrade != CurrentProtocolVersion) - { - LogPrintf("New protocol upgrade version: %d (was %d)\n",mc_gState->m_ProtocolVersionToUpgrade,CurrentProtocolVersion); -// if(mc_gState->m_ProtocolVersionToUpgrade > mc_gState->GetProtocolVersion()) - if( (mc_gState->m_ProtocolVersionToUpgrade > 0) && (mc_gState->IsSupported(mc_gState->m_ProtocolVersionToUpgrade) == 0) ) - { - LogPrintf("NODE SHOULD BE UPGRADED FROM %d TO %d\n",mc_gState->GetProtocolVersion(),mc_gState->m_ProtocolVersionToUpgrade); - } - else - { - if(mc_gState->m_ProtocolVersionToUpgrade != mc_gState->m_NetworkParams->ProtocolVersion()) - { - LogPrintf("NODE IS UPGRADED FROM %d TO %d\n",mc_gState->m_NetworkParams->ProtocolVersion(),mc_gState->m_ProtocolVersionToUpgrade); - mc_gState->m_NetworkParams->m_ProtocolVersion=mc_gState->m_ProtocolVersionToUpgrade;// UPGRADE CODE HERE - mc_gState->m_NetworkParams->SetGlobals(); - SetMultiChainParams(); - } - } - } - else - { - mc_gState->m_ProtocolVersionToUpgrade=0; - } - - return MC_ERR_NOERROR; -} - void MultichainNode_UpdateBlockByHeightList(CBlockIndex *pindex) { if(pindex->nHeight < 0) @@ -1397,7 +1348,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectInsaneFee,bool fAddToWallet) + bool* pfMissingInputs, bool fRejectInsaneFee,CWalletTx *wtx) { AssertLockHeld(cs_main); if (pfMissingInputs) @@ -1675,16 +1626,21 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } - if(fAddToWallet) + int err=MC_ERR_NOERROR; + if(wtx) { - int err=pwalletTxsMain->AddTx(NULL,tx,-1,NULL,-1,0); - if(err) - { - reason=strprintf("Wallet error %d",err); - return state.DoS(0, - error("AcceptToMemoryPool: : AcceptMultiChainTransaction failed %s : %s", hash.ToString(),reason), - REJECT_INVALID, reason); - } + err=pwalletTxsMain->AddTx(NULL,*wtx,-1,NULL,-1,0); + } + else + { + err=pwalletTxsMain->AddTx(NULL,tx,-1,NULL,-1,0); + } + if(err) + { + reason=strprintf("Wallet error %d",err); + return state.DoS(0, + error("AcceptToMemoryPool: : AcceptMultiChainTransaction failed %s : %s", hash.ToString(),reason), + REJECT_INVALID, reason); } permissions_to=mc_gState->m_Permissions->m_MempoolPermissions->GetCount(); @@ -1695,12 +1651,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa pool.addUnchecked(hash, entry); } - if(fAddToWallet) + if(((mc_gState->m_WalletMode & MC_WMD_ADDRESS_TXS) == 0) || (mc_gState->m_WalletMode & MC_WMD_MAP_TXS)) { - if(((mc_gState->m_WalletMode & MC_WMD_ADDRESS_TXS) == 0) || (mc_gState->m_WalletMode & MC_WMD_MAP_TXS)) - { - SyncWithWallets(tx, NULL); - } + SyncWithWallets(tx, NULL); } return true; @@ -1831,7 +1784,7 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) if (block.GetHash() != pindex->GetBlockHash()) return error("ReadBlockFromDisk(CBlock&, CBlockIndex*) : GetHash() doesn't match index"); /* MCHN START */ - VerifyBlockSignature(&block,true); + VerifyBlockSignature(&block,true,false); /* MCHN END */ return true; } @@ -2077,6 +2030,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi CAmount nValueIn = 0; CAmount nFees = 0; int async_count=0; + bool fIsLicenseTokenTransfer=Is_MultiChainLicenseTokenTransfer(tx); vector vSendPermissionFlags; @@ -2106,28 +2060,36 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi CTxDestination addressRet; if(ExtractDestinationScriptValid(coins->vout[prevout.n].scriptPubKey, addressRet)) { - CKeyID *lpKeyID=boost::get (&addressRet); - if(lpKeyID != NULL) - { - if(!mc_gState->m_Permissions->CanSend(NULL,(unsigned char*)(lpKeyID))) + if(!fIsLicenseTokenTransfer) + { + CKeyID *lpKeyID=boost::get (&addressRet); + if(lpKeyID != NULL) { - return state.Invalid(error("CheckInputs() : %s input %d doesn't have send permission", tx.GetHash().ToString(),i)); - } - send_permission_flags=SCRIPT_VERIFY_SKIP_SEND_PERMISSION_CHECK; - async_count++; + if(!mc_gState->m_Permissions->CanSend(NULL,(unsigned char*)(lpKeyID))) + { + return state.Invalid(error("CheckInputs() : %s input %d doesn't have send permission", tx.GetHash().ToString(),i)); + } + send_permission_flags=SCRIPT_VERIFY_SKIP_SEND_PERMISSION_CHECK; + async_count++; + } + else + { + CScriptID *lpScriptID=boost::get (&addressRet); + if(lpScriptID) + { + if(mc_gState->m_Permissions->CanSend(NULL,(unsigned char*)(lpScriptID))) + { + send_permission_flags=SCRIPT_VERIFY_SKIP_SEND_PERMISSION_CHECK; + async_count++; + } + } + } } else { - CScriptID *lpScriptID=boost::get (&addressRet); - if(lpScriptID) - { - if(mc_gState->m_Permissions->CanSend(NULL,(unsigned char*)(lpScriptID))) - { - send_permission_flags=SCRIPT_VERIFY_SKIP_SEND_PERMISSION_CHECK; - async_count++; - } - } - } + send_permission_flags=SCRIPT_VERIFY_SKIP_SEND_PERMISSION_CHECK; + async_count++; + } } } @@ -2341,16 +2303,21 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if(!pindex->kMiner.IsValid() || (setBannedTxs.size() != 0) ) { if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) - return false; + { + return state.DoS(0, error("ConnectBlock() : error on CheckBlock"), + REJECT_INVALID, "bad-check-block"); + } } if(!CheckBlockForUpgardableConstraints(block,state,"maximum-block-size",true)) { - return false; + return state.DoS(100, error("ConnectBlock() : above maximum-block-size"), + REJECT_INVALID, "bad-block-size"); } if(!CheckBlockForUpgardableConstraints(block,state,"maximum-block-sigops",true)) { - return false; + return state.DoS(100, error("ConnectBlock() : above maximum-block-sigops"), + REJECT_INVALID, "bad-block-sigops"); } /* MCHN START */ @@ -2528,6 +2495,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector > vPos; vPos.reserve(block.vtx.size()); blockundo.vtxundo.reserve(block.vtx.size() - 1); + + if(fDebug)LogPrint("mchn","mchn: Checking Block with %d transactions\n",block.vtx.size()); + for (unsigned int i = 0; i < block.vtx.size(); i++) { const CTransaction &tx = block.vtx[i]; @@ -2573,7 +2543,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin { return state.DoS(0, error("ConnectBlock: : AcceptMultiChainTransaction failed %s : %s", tx.GetHash().ToString(),reason), - REJECT_NONSTANDARD, reason); + REJECT_INVALID, reason); } } /* MCHN END */ @@ -2602,7 +2572,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin { if(!AcceptMultiChainTransaction(tx,view,coinbase_offset,true,reason,NULL,NULL)) { - return false; + return state.DoS(0, + error("ConnectBlock: : AcceptMultiChainTransaction failed %s : %s", tx.GetHash().ToString(),reason), + REJECT_INVALID, reason); +// return false; } } } @@ -2833,6 +2806,10 @@ bool static DisconnectTip(CValidationState &state) { if(fDebug)LogPrint("mcblockperf","mchn-block-perf: Rolling back permission and asset databases\n"); mc_gState->m_Permissions->RollBack(old_height-1); mc_gState->m_Assets->RollBack(old_height-1); + if(pMultiChainFilterEngine) + { + pMultiChainFilterEngine->Reset(old_height-1,0); + } MultichainNode_ApplyUpgrades(old_height-1); if(mc_gState->m_WalletMode & MC_WMD_TXS) @@ -2908,6 +2885,10 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; int64_t nTime3; if(fDebug)LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); + if(pMultiChainFilterEngine) + { + pMultiChainFilterEngine->Reset(pindexNew->nHeight-1,1); + } { CCoinsViewCache view(pcoinsTip); CInv inv(MSG_BLOCK, pindexNew->GetBlockHash()); @@ -2948,33 +2929,42 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * if(fDebug)LogPrint("mcblockperf","mchn-block-perf: Wallet, before commit completed (%s)\n",(mc_gState->m_WalletMode & MC_WMD_TXS) ? pwalletTxsMain->Summary() : ""); if(err) { - return error("ConnectTip() : ConnectBlock %s failed, Wtxs BeforeCommit, error: %d", pindexNew->GetBlockHash().ToString(),err); + LogPrintf("ConnectTip() : ConnectBlock %s failed, Wtxs BeforeCommit, error: %d", pindexNew->GetBlockHash().ToString(),err); } CDiskTxPos pos(pindexNew->GetBlockPos(), GetSizeOfCompactSize(pblock->vtx.size())); if(fDebug)LogPrint("mcblockperf","mchn-block-perf: Adding block txs to wallet\n"); for (unsigned int i = 0; i < pblock->vtx.size(); i++) { - const CTransaction &tx = pblock->vtx[i]; - err=pwalletTxsMain->AddTx(NULL,tx,pindexNew->nHeight,&pos,i,pindexNew->GetBlockHash()); - if(err) + if(err == MC_ERR_NOERROR) { - return error("ConnectTip() : ConnectBlock %s failed, Wtxs AddTx %s, error: %d", pindexNew->GetBlockHash().ToString(),tx.GetHash().ToString(),err); + const CTransaction &tx = pblock->vtx[i]; + err=pwalletTxsMain->AddTx(NULL,tx,pindexNew->nHeight,&pos,i,pindexNew->GetBlockHash()); + if(err) + { + LogPrintf("Wallet error when connecting block %s, Tx %s, error: %d\n", pindexNew->GetBlockHash().ToString(),tx.GetHash().ToString(),err); + } + pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } - pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } if(fDebug)LogPrint("mcblockperf","mchn-block-perf: Wallet, commit (%s)\n",(mc_gState->m_WalletMode & MC_WMD_TXS) ? pwalletTxsMain->Summary() : ""); - err=pwalletTxsMain->Commit(NULL); - if(err) + if(err == MC_ERR_NOERROR) { - return error("ConnectTip() : ConnectBlock %s failed, Wtxs Commit, error: %d", pindexNew->GetBlockHash().ToString(),err); - } + err=pwalletTxsMain->Commit(NULL); + if(err) + { + LogPrintf("ConnectTip() : ConnectBlock %s failed, Wtxs Commit, error: %d", pindexNew->GetBlockHash().ToString(),err); + } + } if(fDebug)LogPrint("mcblockperf","mchn-block-perf: Wallet, commit completed (%s)\n",(mc_gState->m_WalletMode & MC_WMD_TXS) ? pwalletTxsMain->Summary() : ""); if(fDebug)LogPrint("mcblockperf","mchn-block-perf: Wallet cleanup\n"); - err=pwalletTxsMain->CleanUpAfterBlock(NULL,pindexNew->nHeight,pindexNew->nHeight-1); - if(err) + if(err == MC_ERR_NOERROR) { - return error("ConnectTip() : ConnectBlock %s failed, Wtxs CleanUpAfterBlock, error: %d", pindexNew->GetBlockHash().ToString(),err); - } + err=pwalletTxsMain->CleanUpAfterBlock(NULL,pindexNew->nHeight,pindexNew->nHeight-1); + if(err) + { + LogPrintf("ConnectTip() : ConnectBlock %s failed, Wtxs CleanUpAfterBlock, error: %d", pindexNew->GetBlockHash().ToString(),err); + } + } /* MCHN END */ // Update chainActive & related variables. @@ -2989,7 +2979,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * } // ... and about transactions that got confirmed: /* MCHN START */ - VerifyBlockSignature(pblock,false); + VerifyBlockSignature(pblock,false,true); MultichainNode_ApplyUpgrades(chainActive.Height()); /* MCHN END */ @@ -3256,7 +3246,7 @@ void UpdateChainMiningStatus(const CBlock &block,CBlockIndex *pindexNew) * Try to make some progress towards making pindexMostWork the active block. * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. */ -static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, CBlock *pblock) { +static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, CBlock *pblock, bool* fInvalidFoundOut) { AssertLockHeld(cs_main); bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); @@ -3327,11 +3317,15 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo } /* MCHN START */ - if (!fInvalidFound) +// if (!fInvalidFound) { mc_gState->m_Permissions->ClearMemPool(); mc_gState->m_Assets->ClearMemPool(); - + if(pMultiChainFilterEngine) + { + pMultiChainFilterEngine->Reset(chainActive.Height(),0); + } + if(fDebug)LogPrint("mcblockperf","mchn-block-perf: Replaying mempool\n"); ReplayMemPool(mempool,0,true); if(fDebug)LogPrint("mcblockperf","mchn-block-perf: Defragmenting mempool hash list\n"); @@ -3378,6 +3372,10 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo } } + if(fInvalidFoundOut) + { + *fInvalidFoundOut=fInvalidFound; + } /* MCHN END */ return true; @@ -3451,7 +3449,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { } /* MCHN END */ - if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) + if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, NULL)) return false; /* MCHN START */ if(pindexMostWork == chainActive.Tip()) @@ -3589,7 +3587,8 @@ string SetLastBlock(uint256 hash,bool *fNotFound) while(pblockindex != chainActive.Tip()) { - if(!ActivateBestChainStep(state,pblockindex,NULL)) + bool fInvalidFound; + if(!ActivateBestChainStep(state,pblockindex,NULL,&fInvalidFound) || fInvalidFound) { string error=state.GetRejectReason(); ActivateBestChain(state); @@ -4552,7 +4551,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, /* MCHN START*/ pindex->dTimeReceived=mc_TimeNowAsDouble(); - if(!VerifyBlockSignature(&block,false)) + if(!VerifyBlockSignature(&block,false,true)) { return false; } @@ -4577,6 +4576,41 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, int nHeight = pindex->nHeight; + if(!VerifyBlockMiner(&block,pindex)) + { + pindex->nStatus |= BLOCK_FAILED_VALID; + setDirtyBlockIndex.insert(pindex); + return false; + } + + CBlockIndex *pindexTip=pindex; + while((pindexTip != NULL) && pindexTip->nHeight > chainActive.Height()) + { + pindexTip=pindexTip->pprev; + } + if(pindexTip == chainActive.Tip()) // If we are on main chain + // If it is the next block, we can check upgradable constraints + // If some blocks are missing and constraints are broken - ignore block + // If it is not an attack and upgrade parameters were indeed upgraded, + // we will download this block again after missing blocks. + { + if(!CheckBlockForUpgardableConstraints(block,state,"maximum-block-size",true)) + { + return false; + } + + if(!CheckBlockForUpgardableConstraints(block,state,"maximum-block-sigops",true)) + { + return false; + } + } + else + { + LogPrintf("Accepted header for block %s found on alternative chain at height %d\n", + pindex->GetBlockHash().ToString().c_str(),pindex->nHeight); + } + + // Write block to history file try { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); @@ -4594,12 +4628,6 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return state.Abort(std::string("System error: ") + e.what()); } - if(!VerifyBlockMiner(&block,pindex)) - { - pindex->nStatus |= BLOCK_FAILED_VALID; - setDirtyBlockIndex.insert(pindex); - return false; - } return true; } @@ -4671,7 +4699,7 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDis /* MCHN START*/ { LOCK(cs_main); - if(!VerifyBlockSignature(pblock,true)) + if(!VerifyBlockSignature(pblock,true,true)) { return false; } @@ -5685,8 +5713,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) - { - LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString()); + { + SeenLocalAddr(addrMe); + LogPrintf("connected to self at %s (me: %s), disconnecting\n", pfrom->addr.ToString(),addrMe.ToStringIPPort().c_str()); pfrom->fDisconnect = true; return true; } diff --git a/src/core/main.h b/src/core/main.h index 2444f3f7..316be9b3 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_MAIN_H @@ -22,6 +22,7 @@ #include "script/script.h" #include "script/sigcache.h" #include "script/standard.h" +#include "filters/multichainfilter.h" #include "utils/sync.h" #include "utils/tinyformat.h" #include "chain/txmempool.h" @@ -46,6 +47,7 @@ class CInv; class CScriptCheck; class CValidationInterface; class CValidationState; +class CWalletTx; struct CBlockTemplate; struct CNodeStateStats; @@ -72,6 +74,8 @@ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 50000; static const unsigned int DEFAULT_MAX_SUCCESSORS_FROM_ONE_NODE = 10; /* MCHN END */ extern int MAX_OP_RETURN_SHOWN; +extern int DEFAULT_ACCEPT_FILTER_TIMEOUT; +extern int DEFAULT_SEND_FILTER_TIMEOUT; extern int MAX_STREAM_QUERY_ITEMS; extern int MAX_FORMATTED_DATA_DEPTH; extern int MIN_BLOCKS_BETWEEN_UPGRADES; @@ -230,7 +234,7 @@ void FlushStateToDisk(); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectInsaneFee=false, bool fAddToWallet=true); + bool* pfMissingInputs, bool fRejectInsaneFee=false, CWalletTx *wtx=NULL); struct CNodeStateStats { diff --git a/src/custom/custom.cpp b/src/custom/custom.cpp index c759d078..6831956e 100644 --- a/src/custom/custom.cpp +++ b/src/custom/custom.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "custom/custom.h" diff --git a/src/custom/custom.h b/src/custom/custom.h index 9d134eb6..778622e9 100644 --- a/src/custom/custom.h +++ b/src/custom/custom.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "core/main.h" diff --git a/src/custom/custom_multichain.cpp b/src/custom/custom_multichain.cpp index b67933ce..e5e63938 100644 --- a/src/custom/custom_multichain.cpp +++ b/src/custom/custom_multichain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "multichain/multichain.h" diff --git a/src/custom/custom_server.cpp b/src/custom/custom_server.cpp index 7fac2277..dba72aa8 100644 --- a/src/custom/custom_server.cpp +++ b/src/custom/custom_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "custom/custom.h" diff --git a/src/entMakefile.am b/src/entMakefile.am new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/entMakefile.am @@ -0,0 +1 @@ + diff --git a/src/entities/asset.cpp b/src/entities/asset.cpp index 1f75f5b4..895c558f 100644 --- a/src/entities/asset.cpp +++ b/src/entities/asset.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "multichain/multichain.h" @@ -228,11 +228,14 @@ int mc_AssetDB::Zero() m_Database = NULL; m_Ledger = NULL; m_MemPool = NULL; + m_TmpRelevantEntities = NULL; + m_ShortTxIDCache = NULL; m_Name[0]=0x00; m_Block=-1; m_PrevPos=-1; m_Pos=0; - m_DBRowCount=0; + m_DBRowCount=0; + m_RollBackPos.Zero(); return MC_ERR_NOERROR; } @@ -305,6 +308,12 @@ int mc_AssetDB::Initialize(const char *name,int mode) m_MemPool=new mc_Buffer; err=m_MemPool->Initialize(m_Ledger->m_KeySize,sizeof(mc_EntityLedgerRow),MC_BUF_MODE_MAP); + m_TmpRelevantEntities=new mc_Buffer; + err=m_TmpRelevantEntities->Initialize(MC_AST_SHORT_TXID_SIZE,MC_AST_SHORT_TXID_SIZE,MC_BUF_MODE_MAP); + + m_ShortTxIDCache=new mc_Buffer; + err=m_ShortTxIDCache->Initialize(MC_AST_SHORT_TXID_SIZE,MC_AST_SHORT_TXID_SIZE+MC_PLS_SIZE_ENTITY,MC_BUF_MODE_MAP); + m_Block=adbBlock; m_PrevPos=adbLastPos; m_Pos=adbLastPos+m_Ledger->m_TotalSize; @@ -425,11 +434,54 @@ int mc_AssetDB::Destroy() delete m_MemPool; } + if(m_TmpRelevantEntities) + { + delete m_TmpRelevantEntities; + } + + if(m_ShortTxIDCache) + { + delete m_ShortTxIDCache; + } + Zero(); return MC_ERR_NOERROR; } +int mc_AssetDB::SetCheckPoint() +{ + m_CheckPointPos=m_Pos; + m_CheckPointMemPoolSize=m_MemPool->GetCount(); + + return MC_ERR_NOERROR; +} + +int mc_AssetDB::RollBackToCheckPoint() +{ + m_Pos=m_CheckPointPos; + m_MemPool->SetCount(m_CheckPointMemPoolSize); + + return MC_ERR_NOERROR; +} + + +int mc_AssetDB::SetRollBackPos(int block,int offset,int inmempool) +{ + m_RollBackPos.m_Block=block; + m_RollBackPos.m_Offset=offset; + m_RollBackPos.m_InMempool=inmempool; + + return MC_ERR_NOERROR; +} + +void mc_AssetDB::ResetRollBackPos() +{ + m_RollBackPos.Zero(); +} + + + int mc_AssetDB::GetEntity(mc_EntityLedgerRow* row) { int err,value_len,mprow; @@ -465,46 +517,55 @@ int mc_AssetDB::GetEntity(mc_EntityLedgerRow* row) row->m_ChainPos=adbRow.m_ChainPos; m_Ledger->Close(); + if(m_RollBackPos.InBlock()) + { + if(m_RollBackPos.IsOut(row->m_Block,row->m_Offset)) + { + result=0; + } + } return result; } - mprow=m_MemPool->Seek((unsigned char*)row); - if(mprow>=0) + if(m_RollBackPos.InBlock() == 0) { - if( (((mc_EntityLedgerRow*)(m_MemPool->GetRow(mprow)))->m_KeyType & MC_ENT_KEYTYPE_MASK) != MC_ENT_KEYTYPE_TXID) + mprow=m_MemPool->Seek((unsigned char*)row); + if(mprow>=0) { - mprow--; - if(mprow>=0) + if( (((mc_EntityLedgerRow*)(m_MemPool->GetRow(mprow)))->m_KeyType & MC_ENT_KEYTYPE_MASK) != MC_ENT_KEYTYPE_TXID) { - if( (((mc_EntityLedgerRow*)(m_MemPool->GetRow(mprow)))->m_KeyType & MC_ENT_KEYTYPE_MASK) != MC_ENT_KEYTYPE_TXID) + mprow--; + if(mprow>=0) { - mprow--; - if(mprow>=0) + if( (((mc_EntityLedgerRow*)(m_MemPool->GetRow(mprow)))->m_KeyType & MC_ENT_KEYTYPE_MASK) != MC_ENT_KEYTYPE_TXID) { - if( (((mc_EntityLedgerRow*)(m_MemPool->GetRow(mprow)))->m_KeyType & MC_ENT_KEYTYPE_MASK) != MC_ENT_KEYTYPE_TXID) + mprow--; + if(mprow>=0) { - mprow--; - if(mprow>=0) + if( (((mc_EntityLedgerRow*)(m_MemPool->GetRow(mprow)))->m_KeyType & MC_ENT_KEYTYPE_MASK) != MC_ENT_KEYTYPE_TXID) { - if( (((mc_EntityLedgerRow*)(m_MemPool->GetRow(mprow)))->m_KeyType & MC_ENT_KEYTYPE_MASK) != MC_ENT_KEYTYPE_TXID) + mprow--; + if(mprow>=0) { - mprow=-1; + if( (((mc_EntityLedgerRow*)(m_MemPool->GetRow(mprow)))->m_KeyType & MC_ENT_KEYTYPE_MASK) != MC_ENT_KEYTYPE_TXID) + { + mprow=-1; + } } } } } } } + if(mprow<0) + { + return 0; + } + memcpy(row,m_MemPool->GetRow(mprow),sizeof(mc_EntityLedgerRow)); + return 1; } - if(mprow<0) - { - return 0; - } - memcpy(row,m_MemPool->GetRow(mprow),sizeof(mc_EntityLedgerRow)); - return 1; } - return 0; } @@ -822,7 +883,7 @@ int mc_AssetDB::InsertEntity(const void* txid, int offset, int entity_type, cons } -int mc_AssetDB::InsertAsset(const void* txid, int offset, uint64_t quantity, const char *name, int multiple, const void* script, size_t script_size, const void* special_script, size_t special_script_size,int update_mempool) +int mc_AssetDB::InsertAsset(const void* txid, int offset, int asset_type, uint64_t quantity, const char *name, int multiple, const void* script, size_t script_size, const void* special_script, size_t special_script_size,int update_mempool) { mc_EntityLedgerRow aldRow; mc_EntityDetails details; @@ -842,7 +903,7 @@ int mc_AssetDB::InsertAsset(const void* txid, int offset, uint64_t quantity, con aldRow.m_Offset=-(m_MemPool->GetCount()+1); } aldRow.m_Quantity=quantity; - aldRow.m_EntityType=MC_ENT_TYPE_ASSET; + aldRow.m_EntityType=asset_type; aldRow.m_FirstPos=-(m_MemPool->GetCount()+1);//-1; // Unconfirmed issue, from 10007 we can create followons for them, so we should differentiate aldRow.m_LastPos=0; aldRow.m_ChainPos=-1; @@ -1509,6 +1570,10 @@ int mc_AssetDB::FindEntityByTxID(mc_EntityDetails *entity,const unsigned char* t if(GetEntity(&aldRow)) { entity->Set(&aldRow); + if(m_TmpRelevantEntities->GetCount()) + { + m_TmpRelevantEntities->Add(entity->GetTxID()+MC_AST_SHORT_TXID_OFFSET); + } return 1; } @@ -1536,6 +1601,10 @@ int mc_AssetDB::FindEntityByShortTxID (mc_EntityDetails *entity, const unsigned if(GetEntity(&aldRow)) { entity->Set(&aldRow); + if(m_TmpRelevantEntities->GetCount()) + { + m_TmpRelevantEntities->Add(entity->GetTxID()+MC_AST_SHORT_TXID_OFFSET); + } return 1; } @@ -1543,6 +1612,26 @@ int mc_AssetDB::FindEntityByShortTxID (mc_EntityDetails *entity, const unsigned } +unsigned char *mc_AssetDB::CachedTxIDFromShortTxID(unsigned char *short_txid) +{ + int row; + row=m_ShortTxIDCache->Seek(short_txid); + if(row >= 0) + { + return m_ShortTxIDCache->GetRow(row)+m_ShortTxIDCache->m_KeySize; + } + + mc_EntityDetails entity; + if(FindEntityByShortTxID(&entity,short_txid)) + { + m_ShortTxIDCache->Add(short_txid,entity.GetTxID()); + return m_ShortTxIDCache->GetRow(m_ShortTxIDCache->GetCount()-1)+m_ShortTxIDCache->m_KeySize; + } + + return NULL; +} + + int mc_AssetDB::FindEntityByRef (mc_EntityDetails *entity,const unsigned char* asset_ref) { mc_EntityLedgerRow aldRow; @@ -1556,6 +1645,10 @@ int mc_AssetDB::FindEntityByRef (mc_EntityDetails *entity,const unsigned char* a if(GetEntity(&aldRow)) { entity->Set(&aldRow); + if(m_TmpRelevantEntities->GetCount()) + { + m_TmpRelevantEntities->Add(entity->GetTxID()+MC_AST_SHORT_TXID_OFFSET); + } return 1; } @@ -1592,6 +1685,10 @@ int mc_AssetDB::FindEntityByName(mc_EntityDetails *entity,const char* name) if(GetEntity(&aldRow)) { entity->Set(&aldRow); + if(m_TmpRelevantEntities->GetCount()) + { + m_TmpRelevantEntities->Add(entity->GetTxID()+MC_AST_SHORT_TXID_OFFSET); + } return 1; } @@ -1622,6 +1719,10 @@ int mc_AssetDB::FindEntityByFollowOn(mc_EntityDetails *entity,const unsigned cha } m_Ledger->Close(); entity->Set(&aldRow); + if(m_TmpRelevantEntities->GetCount()) + { + m_TmpRelevantEntities->Add(entity->GetTxID()+MC_AST_SHORT_TXID_OFFSET); + } return 1; } @@ -1694,6 +1795,11 @@ const unsigned char* mc_EntityDetails::GetRef() int mc_EntityDetails::IsUnconfirmedGenesis() { + if( (m_LedgerRow.m_Block > mc_gState->m_Assets->m_Block) && (m_LedgerRow.m_Offset >= 0) ) // Can happen only when called from filter + { + return 1; + } + return ((int)mc_GetLE(m_Ref+4,4)<0) ? 1 : 0; } @@ -1739,6 +1845,26 @@ int mc_EntityDetails::GetAssetMultiple() return multiple; } +uint32_t mc_EntityDetails::GetFilterType() +{ + uint32_t filter_type=0; + size_t size; + void* ptr; + + ptr=NULL; + ptr=(void*)GetSpecialParam(MC_ENT_SPRM_FILTER_TYPE,&size); + + if(ptr) + { + if(size==sizeof(filter_type)) + { + filter_type=(uint32_t)mc_GetLE(ptr,size); + } + } + + return filter_type; +} + int mc_EntityDetails::IsFollowOn() { if(m_LedgerRow.m_KeyType & MC_ENT_KEYTYPE_FOLLOW_ON) @@ -1959,9 +2085,26 @@ void mc_AssetDB::Dump() uint32_t mc_AssetDB::MaxEntityType() { + if(mc_gState->m_Features->Filters() == 0) + { + return MC_ENT_TYPE_UPGRADE; + } + if(mc_gState->m_Features->LicenseTokens() == 0) + { + return MC_ENT_TYPE_FILTER; + } return MC_ENT_TYPE_MAX; } +int mc_AssetDB::MaxScriptSize() +{ + if(mc_gState->m_Features->Filters() == 0) + { + return MC_ENT_MAX_SCRIPT_SIZE_BEFORE_FILTERS; + } + return MC_ENT_MAX_SCRIPT_SIZE; +} + int mc_AssetDB::MaxStoredIssuers() { return MC_ENT_MAX_STORED_ISSUERS; diff --git a/src/entities/asset.h b/src/entities/asset.h index 0c512cc5..87de27d0 100644 --- a/src/entities/asset.h +++ b/src/entities/asset.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAIN_ASSET_H @@ -31,14 +31,15 @@ -#define MC_ENT_REF_SIZE 10 -#define MC_ENT_REF_PREFIX_SIZE 2 -#define MC_ENT_MAX_NAME_SIZE 32 -#define MC_ENT_MAX_ITEM_KEY_SIZE 256 -#define MC_ENT_MAX_SCRIPT_SIZE 4096 -#define MC_ENT_MAX_FIXED_FIELDS_SIZE 128 -#define MC_ENT_MAX_STORED_ISSUERS 128 -#define MC_ENT_SCRIPT_ALLOC_SIZE 8192 // > MC_ENT_MAX_SCRIPT_SIZE + MC_ENT_MAX_FIXED_FIELDS_SIZE + 27*MC_ENT_MAX_STORED_ISSUERS +#define MC_ENT_REF_SIZE 10 +#define MC_ENT_REF_PREFIX_SIZE 2 +#define MC_ENT_MAX_NAME_SIZE 32 +#define MC_ENT_MAX_ITEM_KEY_SIZE 256 +#define MC_ENT_MAX_SCRIPT_SIZE_BEFORE_FILTERS 4096 +#define MC_ENT_MAX_SCRIPT_SIZE 65536 +#define MC_ENT_MAX_FIXED_FIELDS_SIZE 128 +#define MC_ENT_MAX_STORED_ISSUERS 128 +#define MC_ENT_SCRIPT_ALLOC_SIZE 66000 // > MC_ENT_MAX_SCRIPT_SIZE + MC_ENT_MAX_FIXED_FIELDS_SIZE + 27*MC_ENT_MAX_STORED_ISSUERS #define MC_ENT_KEY_SIZE 32 #define MC_ENT_KEYTYPE_TXID 0x00000001 @@ -54,7 +55,9 @@ #define MC_ENT_TYPE_STREAM 0x02 #define MC_ENT_TYPE_STREAM_MAX 0x0F #define MC_ENT_TYPE_UPGRADE 0x10 -#define MC_ENT_TYPE_MAX 0x10 +#define MC_ENT_TYPE_FILTER 0x11 +#define MC_ENT_TYPE_LICENSE_TOKEN 0x12 +#define MC_ENT_TYPE_MAX 0x12 #define MC_ENT_SPRM_NAME 0x01 #define MC_ENT_SPRM_FOLLOW_ONS 0x02 @@ -67,6 +70,20 @@ #define MC_ENT_SPRM_UPGRADE_PROTOCOL_VERSION 0x42 #define MC_ENT_SPRM_UPGRADE_START_BLOCK 0x43 #define MC_ENT_SPRM_UPGRADE_CHAIN_PARAMS 0x44 +#define MC_ENT_SPRM_FILTER_RESTRICTIONS 0x45 +#define MC_ENT_SPRM_FILTER_CODE 0x46 +#define MC_ENT_SPRM_FILTER_TYPE 0x47 + +#define MC_ENT_SPRM_LICENSE_REQUEST_HASH 0x60 +#define MC_ENT_SPRM_LICENSE_REQUEST_ADDRESS 0x61 +#define MC_ENT_SPRM_LICENSE_CONFIRMATION_TIME 0x62 +#define MC_ENT_SPRM_LICENSE_CONFIRMATION_REF 0x63 +#define MC_ENT_SPRM_LICENSE_PUBKEY 0x69 +#define MC_ENT_SPRM_LICENSE_MIN_VERSION 0x6A +#define MC_ENT_SPRM_LICENSE_MIN_PROTOCOL 0x6B +#define MC_ENT_SPRM_LICENSE_DETAILS 0x6E +#define MC_ENT_SPRM_LICENSE_SIGNATURE 0x6F + #define MC_ENT_SPRM_TIMESTAMP 0x81 #define MC_ENT_SPRM_CHUNK_HASH 0x82 @@ -171,6 +188,7 @@ typedef struct mc_EntityDetails int IsUnconfirmedGenesis(); int GetAssetMultiple(); + uint32_t GetFilterType(); int IsFollowOn(); // int HasFollowOns(); int AllowedFollowOns(); @@ -227,12 +245,17 @@ typedef struct mc_AssetDB mc_EntityLedger *m_Ledger; mc_Buffer *m_MemPool; + mc_Buffer *m_TmpRelevantEntities; + mc_Buffer *m_ShortTxIDCache; char m_Name[MC_PRM_NETWORK_NAME_MAX_SIZE+1]; int m_Block; int64_t m_PrevPos; int64_t m_Pos; + int64_t m_CheckPointPos; + uint64_t m_CheckPointMemPoolSize; int m_DBRowCount; + mc_RollBackPos m_RollBackPos; mc_AssetDB() { @@ -250,12 +273,15 @@ typedef struct mc_AssetDB int Initialize(const char *name,int mode); int InsertEntity(const void* txid, int offset, int entity_type, const void *script,size_t script_size, const void* special_script, size_t special_script_size,int update_mempool); - int InsertAsset(const void* txid, int offset, uint64_t quantity,const char *name,int multiple,const void *script,size_t script_size, const void* special_script, size_t special_script_size,int update_mempool); + int InsertAsset(const void* txid, int offset, int asset_type, uint64_t quantity,const char *name,int multiple,const void *script,size_t script_size, const void* special_script, size_t special_script_size,int update_mempool); int InsertAssetFollowOn(const void* txid, int offset, uint64_t quantity, const void *script,size_t script_size, const void* special_script, size_t special_script_size,const void* original_txid,int update_mempool); int Commit(); int RollBack(int block); int RollBack(); int ClearMemPool(); + + int SetCheckPoint(); + int RollBackToCheckPoint(); int GetEntity(mc_EntityLedgerRow *row); @@ -266,6 +292,11 @@ typedef struct mc_AssetDB int FindEntityByFollowOn(mc_EntityDetails *entity, const unsigned char* txid); int FindEntityByFullRef (mc_EntityDetails *entity, unsigned char* full_ref); + unsigned char *CachedTxIDFromShortTxID(unsigned char *short_txid); + int SetRollBackPos(int block,int offset,int inmempool); + void ResetRollBackPos(); + + void Dump(); mc_Buffer *GetEntityList(mc_Buffer *old_result,const void* txid,uint32_t entity_type); void FreeEntityList(mc_Buffer *entities); @@ -275,6 +306,7 @@ typedef struct mc_AssetDB void RemoveFiles(); uint32_t MaxEntityType(); + int MaxScriptSize(); int MaxStoredIssuers(); //Internal functions diff --git a/src/filters/filter.cpp b/src/filters/filter.cpp new file mode 100644 index 00000000..981ec98a --- /dev/null +++ b/src/filters/filter.cpp @@ -0,0 +1,170 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "filters/filter.h" +#include "filters/filtercallback.h" +#include "filters/watchdog.h" +#include "utils/define.h" +#include "utils/util.h" +#include "v8/v8engine.h" +#include "v8/v8filter.h" +#include + +//using namespace boost::placeholders; + +namespace mc_v8 +{ +class V8Filter; +} + +void mc_Filter::Zero() +{ + m_Impl = nullptr; + m_timeout = 0; +} + +int mc_Filter::Destroy() +{ + if (m_Impl != nullptr) + { + auto v8filter = static_cast(m_Impl); + delete v8filter; + } + this->Zero(); + return MC_ERR_NOERROR; +} + +int mc_Filter::Initialize(std::string &strResult) +{ + strResult.clear(); + m_Impl = new mc_v8::V8Filter(); + return MC_ERR_NOERROR; +} + +void mc_FilterEngine::Zero() +{ + m_Impl = nullptr; + m_runningFilter = nullptr; + m_watchdog = nullptr; + m_filterCallback.ResetCallbackLog(); +} + +int mc_FilterEngine::Destroy() +{ + if (m_Impl != nullptr) + { + auto v8engine = static_cast(m_Impl); + delete v8engine; + } + if (m_watchdog != nullptr) + { + m_watchdog->Shutdown(); + delete m_watchdog; + } + + this->Zero(); + return MC_ERR_NOERROR; +} + +int mc_FilterEngine::Initialize(std::string &strResult) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::Initialize\n"); + strResult.clear(); + auto v8engine = new mc_v8::V8Engine(); + m_Impl = v8engine; + return v8engine->Initialize(&m_filterCallback, strResult); +} + +int mc_FilterEngine::CreateFilter(std::string script, std::string main_name, std::vector &callback_names, + mc_Filter *filter, std::string &strResult) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::CreateFilter\n"); + strResult.clear(); + auto v8engine = static_cast(m_Impl); + filter->Destroy(); + int result = filter->Initialize(strResult); + if (result != MC_ERR_NOERROR || !strResult.empty()) + { + return result; + } + auto v8filter = static_cast(filter->m_Impl); + result = v8engine->CreateFilter(script, main_name, callback_names, v8filter, strResult); + if (result != MC_ERR_NOERROR || !strResult.empty()) + { + filter->Destroy(); + } + return result; +} + +int mc_FilterEngine::CreateFilter(std::string script, std::string main_name, std::vector &callback_names, + mc_Filter *filter, int timeout, std::string &strResult) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::CreateFilter(timeout=%d)\n", timeout); + int result = CreateFilter(script, main_name, callback_names, filter, strResult); + if (result == MC_ERR_NOERROR) + { + filter->SetTimeout(timeout); + } + return result; +} + +int mc_FilterEngine::RunFilter(const mc_Filter *filter, std::string &strResult, bool createCallbackLog, + json_spirit::Array *callbacks) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::RunFilter\n"); + + m_filterCallback.ResetCallbackLog(); + m_filterCallback.SetCreateCallbackLog(createCallbackLog); + auto v8engine = static_cast(m_Impl); + auto v8filter = static_cast(filter->m_Impl); + SetRunningFilter(filter); + int retval = v8engine->RunFilter(v8filter, strResult); + if (createCallbackLog) + { + *callbacks = m_filterCallback.GetCallbackLog(); + } + ResetRunningFilter(); + return retval; +} + +int mc_FilterEngine::RunFilterWithCallbackLog(const mc_Filter *filter, std::string &strResult, + json_spirit::Array *callbacks) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::RunFilterWithCallbackLog\n"); + int retval = this->RunFilter(filter, strResult, true, callbacks); + return retval; +} + +void mc_FilterEngine::TerminateFilter(std::string reason) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::TerminateFilter\n"); + if (m_runningFilter != nullptr) + { + auto v8engine = static_cast(m_Impl); + auto v8filter = static_cast(m_runningFilter->m_Impl); + v8engine->TerminateFilter(v8filter, reason); + } +} + +void mc_FilterEngine::SetRunningFilter(const mc_Filter *filter) +{ + m_runningFilter = filter; + if (m_watchdog == nullptr) + { + m_watchdog = new Watchdog(boost::bind(&mc_FilterEngine::TerminateFilter, this, _1)); + } + m_watchdog->FilterStarted(filter->Timeout()); +} + +void mc_FilterEngine::ResetRunningFilter() +{ + assert(m_watchdog != nullptr); + m_runningFilter = nullptr; + m_watchdog->FilterEnded(); +} diff --git a/src/filters/filter.h b/src/filters/filter.h new file mode 100644 index 00000000..38fd316c --- /dev/null +++ b/src/filters/filter.h @@ -0,0 +1,155 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef MULTICHAIN_FILTER_H +#define MULTICHAIN_FILTER_H + +#include "filters/filtercallback.h" +#include "json/json_spirit.h" + +class mc_FilterEngine; +class Watchdog; + +/** + * A user-defined transaction filter. + */ +class mc_Filter +{ + friend class mc_FilterEngine; + + public: + mc_Filter() + { + Zero(); + } + + ~mc_Filter() + { + Destroy(); + } + + void Zero(); + int Destroy(); + + int Timeout() const + { + return m_timeout; + } + + void SetTimeout(int timeout) + { + m_timeout = timeout; + } + + /** + * Initialize the transaction filter. + * + * @param strResult Reason for failure if unsuccessful. + * @return MC_ERR_NOERROR if successful, MC_ERR_INTERNAL_ERROR if not. + */ + int Initialize(std::string &strResult); + + private: + void *m_Impl; + int m_timeout; + +}; // class mc_Filter + +/** + * An environment for creating and defining transaction filters. + */ +class mc_FilterEngine +{ + public: + mc_FilterEngine() + { + Zero(); + } + + ~mc_FilterEngine() + { + Destroy(); + } + + void Zero(); + int Destroy(); + + /** + * Initialize the environment. + * + * @param strResult Reason for failure if unsuccessful. + * @return MC_ERR_NOERROR if successful, MC_ERR_INTERNAL_ERROR if not. + */ + int Initialize(std::string &strResult); + + /** + * Initialize a user-defined transaction filter. + * + * @param script The filter JS code. + * @param main_name The expected name of the filtering function in the script. + * @param callback_names A list of callback function names to register for the filter. + * If empty, register no callback functions. + * @param filter The user-defined transaction filter to initialize. + * @param strResult Reason for failure if unsuccessful. + * @return MC_ERR_INTERNAL_ERROR if the engine failed, MC_ERR_NOERROR otherwise. + */ + int CreateFilter(std::string script, std::string main_name, std::vector &callback_names, + mc_Filter *filter, std::string &strResult); + + /** + * Create a filter with an execution timeout. + * + * @param script The filter JS code. + * @param main_name The expected name of the filtering function in the script. + * @param callback_names A list of callback function names to register for the filter. + * If empty, register no callback functions. + * @param filter The user-defined transaction filter to initialize. + * @param timeout The execution timeout, in milliseconds. + * @param strResult Reason for failure if unsuccessful. + * @return MC_ERR_INTERNAL_ERROR if the engine failed, MC_ERR_NOERROR otherwise. + */ + int CreateFilter(std::string script, std::string main_name, std::vector &callback_names, + mc_Filter *filter, int timeout, std::string &strResult); + + /** + * Run the filter function in the JS script. + * + * @param filter The user-defined transaction filter to use. + * @param strResult Reason for script failure or transaction rejection. + * @param createCallbackLog Indicate if a callbacklog is requested. + * @param callbacks An array of RPC callback call data. + * @return MC_ERR_INTERNAL_ERROR if the engine failed, MC_ERR_NOERROR otherwise. + */ + int RunFilter(const mc_Filter *filter, std::string &strResult, bool createCallbackLog = false, + json_spirit::Array *callbacks = nullptr); + + /** + * Run the filter function in the JS script. + * + * This variant provides detailed data about RPC callback calls: parameters, results, success/failure and errors. + * + * @param filter The user-defined transaction filter to use. + * @param strResult Reason for script failure or transaction rejection. + * @param callbacks An array of RPC callback call data. + * @return MC_ERR_INTERNAL_ERROR if the engine failed, MC_ERR_NOERROR otherwise. + */ + int RunFilterWithCallbackLog(const mc_Filter *filter, std::string &strResult, json_spirit::Array *callbacks); + + /** + * Abort the currently running filter (if any). + * + * @param reason The reason the filter is being terminated. + */ + void TerminateFilter(std::string reason); + + private: + void *m_Impl; + const mc_Filter *m_runningFilter; + Watchdog *m_watchdog; + FilterCallback m_filterCallback; + + void SetRunningFilter(const mc_Filter *filter); + void ResetRunningFilter(); +}; // class mc_FilterEngine + +#endif /* MULTICHAIN_FILTER_H */ diff --git a/src/filters/filter_cold.cpp b/src/filters/filter_cold.cpp new file mode 100644 index 00000000..1260f603 --- /dev/null +++ b/src/filters/filter_cold.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "filters/filter.h" +#include "utils/define.h" +#include "utils/util.h" + +void mc_Filter::Zero() +{ + m_Impl = nullptr; +} + +int mc_Filter::Destroy() +{ + this->Zero(); + return MC_ERR_NOERROR; +} + +int mc_Filter::Initialize(std::string &strResult) +{ + strResult.clear(); + return MC_ERR_NOERROR; +} + +void mc_FilterEngine::Zero() +{ + m_Impl = nullptr; +} + +int mc_FilterEngine::Destroy() +{ + this->Zero(); + return MC_ERR_NOERROR; +} + +int mc_FilterEngine::Initialize(std::string &strResult) +{ + strResult.clear(); + return MC_ERR_NOERROR; +} + +int mc_FilterEngine::CreateFilter(std::string, std::string, std::vector &, mc_Filter *, + std::string &strResult) +{ + strResult.clear(); + return MC_ERR_NOERROR; +} + +int mc_FilterEngine::CreateFilter(std::string, std::string, std::vector &, mc_Filter *, int, + std::string &strResult) +{ + strResult.clear(); + return MC_ERR_NOERROR; +} + +int mc_FilterEngine::RunFilter(const mc_Filter *, std::string &strResult, bool, json_spirit::Array *) +{ + strResult.clear(); + return MC_ERR_NOERROR; +} + +int mc_FilterEngine::RunFilterWithCallbackLog(const mc_Filter *, std::string &strResult, json_spirit::Array *callbacks) +{ + strResult.clear(); + callbacks->clear(); + return MC_ERR_NOERROR; +} + +void mc_FilterEngine::TerminateFilter(std::string) +{ +} diff --git a/src/filters/filter_win.cpp b/src/filters/filter_win.cpp new file mode 100644 index 00000000..42cdb29a --- /dev/null +++ b/src/filters/filter_win.cpp @@ -0,0 +1,201 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "filters/filter.h" +#include "filters/filtercallback.h" +#include "filters/watchdog.h" +#include "chainparams/state.h" +#include "utils/define.h" +#include "utils/util.h" +#include "v8_win/v8_win.h" +#include + +//using namespace boost::placeholders; + +const size_t RESULT_SIZE = 4096; + +const char **vec2cstrs(const std::vector& vec, size_t &ncstrs) +{ + ncstrs = vec.size(); + const char **cstrs = new const char*[ncstrs]; + for (size_t i = 0; i < ncstrs; ++i) + { + cstrs[i] = vec[i].c_str(); + } + return cstrs; +} + +void mc_Filter::Zero() +{ + m_Impl = nullptr; + m_timeout = 0; +} + +int mc_Filter::Destroy() +{ + if (m_Impl != nullptr) + { + auto v8filter = static_cast(m_Impl); + V8Filter_Delete(&v8filter); + } + this->Zero(); + return MC_ERR_NOERROR; +} + +int mc_Filter::Initialize(std::string &strResult) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_Filter::Initialize\n"); + strResult.clear(); + m_Impl = V8Filter_Create(); + return MC_ERR_NOERROR; +} + +void mc_FilterEngine::Zero() +{ + m_Impl = nullptr; + m_runningFilter = nullptr; + m_watchdog = nullptr; + m_filterCallback.ResetCallbackLog(); +} + +int mc_FilterEngine::Destroy() +{ + if (m_Impl != nullptr) + { + auto v8engine = static_cast(m_Impl); + V8Engine_Delete(&v8engine); + } + if (m_watchdog != nullptr) + { + m_watchdog->Shutdown(); + delete m_watchdog; + } + + this->Zero(); + return MC_ERR_NOERROR; +} + +int mc_FilterEngine::Initialize(std::string &strResult) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::Initialize\n"); + strResult.clear(); + auto v8engine = V8Engine_Create(); + m_Impl = v8engine; + auto dataDir = GetDataDir(); + if (fDebug) + LogPrint("v8filter", "v8filter: dataDir=%s\n", dataDir.string()); + char result[RESULT_SIZE]; + auto v8filterCallback = reinterpret_cast(&m_filterCallback); + int retval = V8Engine_Initialize(v8engine, v8filterCallback, dataDir.string().c_str(), fDebug, result); + if (fDebug) + LogPrint("v8filter", "v8filter: retval=%d result=%s\n", retval, result); + strResult = result; + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::Initialize - done\n"); + return retval; +} + +int mc_FilterEngine::CreateFilter(std::string script, std::string main_name, std::vector &callback_names, + mc_Filter *filter, std::string &strResult) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::CreateFilter\n"); + strResult.clear(); + auto v8engine = static_cast(m_Impl); + filter->Destroy(); + int retval = filter->Initialize(strResult); + if (retval != MC_ERR_NOERROR || !strResult.empty()) + { + return retval; + } + size_t n_callbackNames; + const char **callbackNames = vec2cstrs(callback_names, n_callbackNames); + auto v8filter = static_cast(filter->m_Impl); + char result[RESULT_SIZE]; + retval = V8Engine_CreateFilter(v8engine, script.c_str(), main_name.c_str(), callbackNames, n_callbackNames, + v8filter, mc_gState->m_Features->FilterLimitedMathSet(), mc_gState->m_Features->FixedJSDateFunctions(), result); + delete [] callbackNames; + if (fDebug) + LogPrint("v8filter", "v8filter: retval=%d result=%s\n", retval, result); + if (retval != MC_ERR_NOERROR || (result != nullptr && strlen(result) > 0)) + { + filter->Destroy(); + } + strResult = result; + return retval; +} + +int mc_FilterEngine::CreateFilter(std::string script, std::string main_name, std::vector &callback_names, + mc_Filter *filter, int timeout, std::string &strResult) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::CreateFilter(timeout=%d)\n", timeout); + int result = CreateFilter(script, main_name, callback_names, filter, strResult); + if (result == MC_ERR_NOERROR) + { + filter->SetTimeout(timeout); + } + return result; +} + +int mc_FilterEngine::RunFilter(const mc_Filter *filter, std::string &strResult, bool createCallbackLog, + json_spirit::Array *callbacks) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::RunFilter\n"); + + m_filterCallback.ResetCallbackLog(); + m_filterCallback.SetCreateCallbackLog(createCallbackLog); + auto v8engine = static_cast(m_Impl); + auto v8filter = static_cast(filter->m_Impl); + SetRunningFilter(filter); + char result[RESULT_SIZE]; + int retval = V8Engine_RunFilter(v8engine, v8filter, result); + if (createCallbackLog) + { + *callbacks = m_filterCallback.GetCallbackLog(); + } + ResetRunningFilter(); + strResult = result; + return retval; +} + +int mc_FilterEngine::RunFilterWithCallbackLog(const mc_Filter *filter, std::string &strResult, + json_spirit::Array *callbacks) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::RunFilterWithCallbackLog\n"); + int retval = this->RunFilter(filter, strResult, true, callbacks); + return retval; +} + +void mc_FilterEngine::TerminateFilter(std::string reason) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: mc_FilterEngine::TerminateFilter\n"); + if (m_runningFilter != nullptr) + { + auto v8engine = static_cast(m_Impl); + auto v8filter = static_cast(m_runningFilter->m_Impl); + V8Engine_TerminateFilter(v8engine, v8filter, reason.c_str()); + } +} + +void mc_FilterEngine::SetRunningFilter(const mc_Filter *filter) +{ + m_runningFilter = filter; + if (m_watchdog == nullptr) + { + m_watchdog = new Watchdog(boost::bind(&mc_FilterEngine::TerminateFilter, this, _1)); + } + m_watchdog->FilterStarted(filter->Timeout()); +} + +void mc_FilterEngine::ResetRunningFilter() +{ + assert(m_watchdog != nullptr); + m_runningFilter = nullptr; + m_watchdog->FilterEnded(); +} diff --git a/src/filters/filtercallback.cpp b/src/filters/filtercallback.cpp new file mode 100644 index 00000000..b71854cb --- /dev/null +++ b/src/filters/filtercallback.cpp @@ -0,0 +1,136 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "filters/filtercallback.h" +#include "rpc/rpcserver.h" +#include "utils/util.h" +#include "json/json_spirit_ubjson.h" +#include "json/json_spirit_writer.h" + +const int MAX_DEPTH = 100; +#define CALLBACK_LOOKUP(name) { #name, &name } + +static std::map FilterCallbackFunctions{ + CALLBACK_LOOKUP(getfiltertxid), + CALLBACK_LOOKUP(getfiltertransaction), + CALLBACK_LOOKUP(getfilterstreamitem), + CALLBACK_LOOKUP(getfilterassetbalances), + CALLBACK_LOOKUP(setfilterparam), + CALLBACK_LOOKUP(getfiltertxinput), + CALLBACK_LOOKUP(getlastblockinfo), + CALLBACK_LOOKUP(getassetinfo), + CALLBACK_LOOKUP(getstreaminfo), + CALLBACK_LOOKUP(verifypermission), + CALLBACK_LOOKUP(verifymessage) +}; + +#ifdef WIN32 +void FilterCallback::UbjCallback(const char *name, Blob_t* argsBlob, Blob_t* resultBlob) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: UbjCallback(name=%s)\n", name); + + int err; + Array jspArgs = ubjson_read(Blob_Data(argsBlob), Blob_DataSize(argsBlob), MAX_DEPTH, &err).get_array(); + if (fDebug) + LogPrint("v8filter", "v8filter: jspArgs[%d]\n", jspArgs.size()); + Value jspResult; + if (fDebug) + LogPrint("v8filter", "v8filter: About to call native function\n"); + try + { + jspResult = FilterCallbackFunctions[name](jspArgs, false); + this->CreateCallbackLog(name, jspArgs, jspResult); + } + catch (Object &e) + { + this->CreateCallbackLogError(name, jspArgs, e); + } + catch (exception &e) + { + this->CreateCallbackLogError(name, jspArgs, e); + } + + if (fDebug) + LogPrint("v8filter", "v8filter: native function retruned jspResult=%s\n", json_spirit::write(jspResult)); + + mc_Script script; + script.AddElement(); + ubjson_write(jspResult, &script, MAX_DEPTH); + size_t resultSize; + const unsigned char *result_ = script.GetData(0, &resultSize); + Blob_Set(resultBlob, result_, resultSize); + + if (fDebug) + LogPrint("v8filter", "v8filter: UbjCallback - done resultSize=%d\n", Blob_DataSize(resultBlob)); +} +#else +void FilterCallback::JspCallback(string name, Array args, Value &result) +{ + result = Value::null; + try + { + result = FilterCallbackFunctions[name](args, false); + this->CreateCallbackLog(name, args, result); + } + catch (Object &e) + { + this->CreateCallbackLogError(name, args, e); + } + catch (exception &e) + { + this->CreateCallbackLogError(name, args, e); + } +} +#endif // WIN32 + +void FilterCallback::CreateCallbackLog(string name, Array args, Value result) +{ + if (!m_createCallbackLog) + return; + + Object callbackData; + callbackData.push_back(Pair("method", name)); + callbackData.push_back(Pair("params", args)); + + bool success = true; + if (result.type() == obj_type) + { + auto obj = result.get_obj(); + auto it = std::find_if(obj.begin(), obj.end(), [](const Pair &pair) -> bool { return pair.name_ == "code"; }); + success = (it == obj.end()); + } + callbackData.push_back(Pair("success", success)); + callbackData.push_back(Pair(success ? "result" : "error", result)); + m_callbackLog.push_back(callbackData); +} + +void FilterCallback::CreateCallbackLogError(string name, Array args, Object &e) +{ + if (!m_createCallbackLog) + return; + + Object callbackData; + callbackData.push_back(Pair("method", name)); + callbackData.push_back(Pair("params", args)); + + callbackData.push_back(json_spirit::Pair("success", false)); + callbackData.push_back(json_spirit::Pair("error", e)); + + m_callbackLog.push_back(callbackData); +} + +void FilterCallback::CreateCallbackLogError(string name, Array args, exception &e) +{ + if (!m_createCallbackLog) + return; + + Object callbackData; + callbackData.push_back(Pair("method", name)); + callbackData.push_back(Pair("params", args)); + + callbackData.push_back(json_spirit::Pair("success", false)); + callbackData.push_back(json_spirit::Pair("error", e.what())); + + m_callbackLog.push_back(callbackData); +} diff --git a/src/filters/filtercallback.h b/src/filters/filtercallback.h new file mode 100644 index 00000000..91f53fba --- /dev/null +++ b/src/filters/filtercallback.h @@ -0,0 +1,75 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef FILTERCALLBACK_H +#define FILTERCALLBACK_H + +#include "filters/ifiltercallback.h" +#include "json/json_spirit.h" + +/** + * A filter callback object that can create a callback log for debugging. + */ +class FilterCallback : public IFilterCallback +{ + public: + virtual ~FilterCallback() override + { + } + + /** + * Get the debugging callback log. + */ + json_spirit::Array GetCallbackLog() const + { + return m_callbackLog; + } + + /** + * Set the flasg for creating a debugging callback log. + * + * @param value The value to set to. + */ + void SetCreateCallbackLog(bool value = true) + { + m_createCallbackLog = value; + } + + /** + * Clear the debugging callback log. + */ + void ResetCallbackLog() + { + m_callbackLog.clear(); + } + +#ifdef WIN32 + /** + * Callback using UBJSON to pass arguments and a return value. + * + * @param name The name of the API function to invoke. + * @param argsBlob The UBJSON content of the function arguments. + * @param resultBlob The UBJSON content of the function return value. + */ + void UbjCallback(const char *name, Blob_t* argsBlob, Blob_t* resultBlob) override; +#else + /** + * Callback using json_spirit to pass arguments and a return value. + * + * @param name The name of the API function to invoke. + * @param args The function arguments. + * @param result The function return value. + */ + void JspCallback(std::string name, json_spirit::Array args, json_spirit::Value &result) override; +#endif // WIN32 + + private: + json_spirit::Array m_callbackLog; + bool m_createCallbackLog = false; + + void CreateCallbackLog(std::string name, json_spirit::Array args, json_spirit::Value result); + void CreateCallbackLogError(std::string name, json_spirit::Array args, json_spirit::Object &e); + void CreateCallbackLogError(std::string name, json_spirit::Array args, std::exception &e); +}; // class FilterCallback + +#endif // FILTERCALLBACK_H diff --git a/src/filters/filtercallback_cold.cpp b/src/filters/filtercallback_cold.cpp new file mode 100644 index 00000000..e1906130 --- /dev/null +++ b/src/filters/filtercallback_cold.cpp @@ -0,0 +1,26 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "filters/filtercallback.h" + +#ifdef WIN32 +void FilterCallback::UbjCallback(const char *, Blob_t*, Blob_t*) +{ +} +#else +void FilterCallback::JspCallback(std::string, json_spirit::Array, json_spirit::Value &) +{ +} +#endif // WIN32 + +void FilterCallback::CreateCallbackLog(std::string, json_spirit::Array, json_spirit::Value) +{ +} + +void FilterCallback::CreateCallbackLogError(std::string, json_spirit::Array, json_spirit::Object &) +{ +} + +void FilterCallback::CreateCallbackLogError(std::string, json_spirit::Array, std::exception &) +{ +} diff --git a/src/filters/ifiltercallback.h b/src/filters/ifiltercallback.h new file mode 100644 index 00000000..7019e482 --- /dev/null +++ b/src/filters/ifiltercallback.h @@ -0,0 +1,46 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef IFILTERCALLBACK_H +#define IFILTERCALLBACK_H + +#include + +#ifndef WIN32 +#include "json/json_spirit.h" +#else +#include "v8_win/v8_win.h" +#endif // WIN32 + +/** + * Interface for platform-dependent filter callback API functions. + */ +class IFilterCallback +{ + public: +#ifdef WIN32 + /** + * Callback using UBJSON to pass arguments and a return value. + * + * @param name The name of the API function to invoke. + * @param argsBlob The UBJSON content of the function arguments. + * @param resultBlob The UBJSON content of the function return value. + */ + virtual void UbjCallback(const char *name, Blob_t* argsBlob, Blob_t* resultBlob) = 0; +#else + /** + * Callback using json_spirit to pass arguments and a return value. + * + * @param name The name of the API function to invoke. + * @param args The function arguments. + * @param result The function return value. + */ + virtual void JspCallback(std::string name, json_spirit::Array args, json_spirit::Value &result) = 0; +#endif // WIN32 + + virtual ~IFilterCallback() + { + } +}; // class IFilterCallback + +#endif // IFILTERCALLBACK_H diff --git a/src/filters/multichainfilter.cpp b/src/filters/multichainfilter.cpp new file mode 100644 index 00000000..4a89908d --- /dev/null +++ b/src/filters/multichainfilter.cpp @@ -0,0 +1,606 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "filters/multichainfilter.h" +#include "filters/filter.h" + +using namespace std; + +int mc_MultiChainFilterParams::Zero() +{ + m_MaxShownData=-1; + m_Compatibility=MC_VCM_NONE; + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilterParams::Init() +{ + Zero(); + m_Compatibility=mc_gState->m_Compatibility; + mc_gState->m_Compatibility=MC_VCM_NONE; + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilterParams::Close() +{ + mc_gState->m_Compatibility=m_Compatibility; + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilterParams::Destroy() +{ + return Zero(); +} + +std::vector mc_FillRelevantFilterEntitities(const unsigned char *ptr, size_t value_size) +{ + std::vector entities; + + if(ptr) + { + for(int i=0;i<(int)value_size/MC_AST_SHORT_TXID_SIZE;i++) + { + uint160 hash=0; + memcpy(&hash,ptr+i*MC_AST_SHORT_TXID_SIZE,MC_AST_SHORT_TXID_SIZE); + entities.push_back(hash); + } + } + + return entities; +} + +int mc_MultiChainFilter::Zero() +{ + m_RelevantEntities.clear(); + m_CreateError="Not Initialized"; + m_FilterType=MC_FLT_TYPE_TX; + m_FilterCaption="Unknown"; + m_FilterCode[0]=0x00; + m_FilterAddress=0; + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilter::Destroy() +{ + Zero(); + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilter::Initialize(const unsigned char* short_txid) +{ + size_t value_size; + unsigned char *ptr; + + m_FilterAddress=0; + memcpy(&m_FilterAddress,short_txid,MC_AST_SHORT_TXID_SIZE); + + if(mc_gState->m_Assets->FindEntityByShortTxID(&m_Details,short_txid) == 0) + { + return MC_ERR_NOT_FOUND; + } + + uint256 txid; + txid=*(uint256*)m_Details.GetTxID(); + if(m_Details.m_Name[0]) + { + m_FilterCaption=strprintf("%s", + m_Details.m_Name); + } + else + { + m_FilterCaption=strprintf("(txid %s)", + txid.ToString().c_str()); + } + + ptr=(unsigned char *)m_Details.GetSpecialParam(MC_ENT_SPRM_FILTER_TYPE,&value_size); + + if(ptr) + { + if( (value_size <=0) || (value_size > 4) ) + { + return MC_ERR_ERROR_IN_SCRIPT; + } + m_FilterType=mc_GetLE(ptr,value_size); + } + + switch(m_FilterType) + { + case MC_FLT_TYPE_TX: + m_MainName=MC_FLT_MAIN_NAME_TX; + break; + case MC_FLT_TYPE_STREAM: + m_MainName=MC_FLT_MAIN_NAME_STREAM; + break; + default: + m_CreateError="Unsupported filter type"; + return MC_ERR_NOT_SUPPORTED; + } + + if(m_FilterType == MC_FLT_TYPE_TX) + { + ptr=(unsigned char *)m_Details.GetSpecialParam(MC_ENT_SPRM_FILTER_RESTRICTIONS,&value_size); + + if(ptr) + { + if(value_size % MC_AST_SHORT_TXID_SIZE) + { + return MC_ERR_ERROR_IN_SCRIPT; + } + m_RelevantEntities=mc_FillRelevantFilterEntitities(ptr, value_size); + } + } + + + ptr=(unsigned char *)m_Details.GetSpecialParam(MC_ENT_SPRM_FILTER_CODE,&value_size); + + if(ptr) + { + memcpy(m_FilterCode,ptr,value_size); + m_FilterCode[value_size]=0x00; + } + else + { + m_CreateError="Empty filter code"; + } + return MC_ERR_NOERROR; +} + +bool mc_MultiChainFilter::HasRelevantEntity(set & sRelevantEntities) +{ + if(m_RelevantEntities.size() == 0) + { + return true; + } + for(int i=0;i<(int)m_RelevantEntities.size();i++) + { + if(sRelevantEntities.find(m_RelevantEntities[i]) != sRelevantEntities.end()) + { + return true; + } + } + return false; +} + +int mc_MultiChainFilterEngine::Zero() +{ + m_Filters.clear(); + m_TxID=0; + m_Workers=NULL; + m_CallbackNames.clear(); + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilterEngine::Destroy() +{ + for(int i=0;i<(int)m_Filters.size();i++) + { + if(m_Workers) + { + mc_Filter *worker=*(mc_Filter **)m_Workers->GetRow(i); + delete worker; + } + m_Filters[i].Destroy(); + } + + if(m_Workers) + { + delete m_Workers; + } + + + Zero(); + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilterEngine::GetAcceptTimeout() +{ + return GetArg("-acceptfiltertimeout",DEFAULT_ACCEPT_FILTER_TIMEOUT); +} + +int mc_MultiChainFilterEngine::GetSendTimeout() +{ + int accept_timeout=GetAcceptTimeout(); + int timeout=GetArg("-sendfiltertimeout",DEFAULT_SEND_FILTER_TIMEOUT); + if(accept_timeout > 0) + { + if(accept_timeout < timeout) + { + timeout=accept_timeout; + } + } + return timeout; +} + +int mc_MultiChainFilterEngine::SetTimeout(int timeout) +{ + for(int i=0;i<(int)m_Filters.size();i++) + { + mc_Filter *worker=*(mc_Filter **)m_Workers->GetRow(i); + worker->SetTimeout(timeout); + } + return MC_ERR_NOERROR; +} + + +int mc_MultiChainFilterEngine::Add(const unsigned char* short_txid,int for_block) +{ + int err; + mc_MultiChainFilter filter; + + err=filter.Initialize(short_txid); + if(err) + { + LogPrintf("Couldn't add filter with short txid %s, error: %d\n",filter.m_FilterAddress.ToString().c_str(),err); + return err; + } + + m_Filters.push_back(filter); + mc_Filter *worker=new mc_Filter; + m_Workers->Add(&worker); + + err=pFilterEngine->CreateFilter(m_Filters.back().m_FilterCode,m_Filters.back().m_MainName.c_str(), + m_CallbackNames[m_Filters.back().m_FilterType],worker,(for_block == 0) ? GetAcceptTimeout() : 0,m_Filters.back().m_CreateError); + if(err) + { + LogPrintf("Couldn't create filter with short txid %s, error: %d\n",filter.m_FilterAddress.ToString().c_str(),err); + m_Workers->SetCount(m_Workers->GetCount()-1); + m_Filters.pop_back(); + return err; + } + + if(fDebug)LogPrint("filter","filter: Filter added: %s\n",m_Filters.back().m_FilterCaption.c_str()); + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilterEngine::Reset(int block,int for_block) +{ + int filter_block; + int err; + + if(m_Filters.size() == 0) + { + return MC_ERR_NOERROR; + } + + filter_block=m_Filters.back().m_Details.m_LedgerRow.m_Block; + if(filter_block<0) + { + filter_block=block+1; + } + while( (m_Filters.size()>0) && (filter_block > block) ) + { + if(fDebug)LogPrint("filter","filter: Filter rolled back: %s\n",m_Filters.back().m_FilterCaption.c_str()); + mc_Filter *worker=*(mc_Filter **)m_Workers->GetRow(m_Workers->GetCount()-1); + worker->Destroy(); + m_Workers->SetCount(m_Workers->GetCount()-1); + m_Filters.back().Destroy(); + m_Filters.pop_back(); + if(m_Filters.size()>0) + { + filter_block=m_Filters.back().m_Details.m_LedgerRow.m_Block; + if(filter_block<0) + { + filter_block=block+1; + } + } + } + + for(int i=0;i<(int)m_Filters.size();i++) + { + mc_Filter *worker=*(mc_Filter **)m_Workers->GetRow(i); + + err=pFilterEngine->CreateFilter(m_Filters[i].m_FilterCode,m_Filters[i].m_MainName,m_CallbackNames[m_Filters[i].m_FilterType], + worker,m_Filters[i].m_CreateError); + if(err) + { + LogPrintf("Couldn't prepare filter %s, error: %d\n",m_Filters[i].m_FilterCaption.c_str(),err); + return err; + } + } + + if(for_block == 0) + { + SetTimeout(GetAcceptTimeout()); + } + + if(fDebug)LogPrint("filter","filter: Filter engine reset\n"); + return MC_ERR_NOERROR; +} +int mc_MultiChainFilterEngine::NoStreamFilters() +{ + if(mc_gState->m_Features->StreamFilters() == 0) + { + return 1; + } + + for(int i=0;i<(int)m_Filters.size();i++) + { + if( (m_Filters[i].m_FilterType == MC_FLT_TYPE_STREAM) && (m_Filters[i].m_CreateError.size() == 0) ) + { + return 0; + } + } + + return 1; +} + +int mc_MultiChainFilterEngine::RunStreamFilters(const CTransaction& tx,int vout, unsigned char *stream_short_txid,int block,int offset,std::string &strResult,mc_MultiChainFilter **lppFilter,int *applied) +{ + if(mc_gState->m_Features->StreamFilters() == 0) + { + return MC_ERR_NOERROR; + } + + int err=MC_ERR_NOERROR; + strResult=""; + m_Tx=tx; + m_TxID=m_Tx.GetHash(); + m_Vout=vout; + m_Params.Init(); + + unsigned char *stream_entity_txid=mc_gState->m_Assets->CachedTxIDFromShortTxID(stream_short_txid); + + if(applied) + { + *applied=0; + } + + if(stream_entity_txid == NULL) + { + goto exitlbl; + } + + mc_gState->m_Permissions->SetRollBackPos(block,offset,(offset != 0) ? 1 : 0); + mc_gState->m_Assets->SetRollBackPos(block,offset,(offset != 0) ? 1 : 0); + + for(int i=0;i<(int)m_Filters.size();i++) + { + if( (m_Filters[i].m_FilterType == MC_FLT_TYPE_STREAM) && (m_Filters[i].m_CreateError.size() == 0) ) + { + if(mc_gState->m_Permissions->FilterApproved(stream_entity_txid,&(m_Filters[i].m_FilterAddress))) + { + mc_Filter *worker=*(mc_Filter **)m_Workers->GetRow(i); + err=pFilterEngine->RunFilter(worker,strResult); + if(err) + { + LogPrintf("Error while running filter %s, error: %d\n",m_Filters[i].m_FilterCaption.c_str(),err); + goto exitlbl; + } + if(strResult.size()) + { + if(lppFilter) + { + *lppFilter=&(m_Filters[i]); + } + if(fDebug)LogPrint("filter","filter: %s: %s\n",m_Filters[i].m_FilterCaption.c_str(),strResult.c_str()); + + goto exitlbl; + } + if(fDebug)LogPrint("filter","filter: Tx %s accepted, filter: %s\n",m_TxID.ToString().c_str(),m_Filters[i].m_FilterCaption.c_str()); + if(applied) + { + *applied+=1; + } + } + } + } + +exitlbl: + + mc_gState->m_Assets->ResetRollBackPos(); + mc_gState->m_Permissions->ResetRollBackPos(); + m_Params.Close(); + m_TxID=0; + m_Vout=-1; + return err; +} + +int mc_MultiChainFilterEngine::RunTxFilters(const CTransaction& tx,std::set & sRelevantEntities,std::string &strResult,mc_MultiChainFilter **lppFilter,int *applied) +{ + int err=MC_ERR_NOERROR; + strResult=""; + m_Tx=tx; + m_TxID=m_Tx.GetHash(); + m_Vout=-1; + m_Params.Init(); + + if(applied) + { + *applied=0; + } + int failure=0; + if(fDebug)LogPrint("filter","filter: Starting filtering for tx %s\n",tx.GetHash().ToString().c_str()); + for(int i=0;i<(int)m_Filters.size();i++) + { + if( (m_Filters[i].m_FilterType == MC_FLT_TYPE_TX) && (m_Filters[i].m_CreateError.size() == 0) ) + { + if(mc_gState->m_Permissions->FilterApproved(NULL,&(m_Filters[i].m_FilterAddress))) + { + if(m_Filters[i].HasRelevantEntity(sRelevantEntities)) + { + mc_Filter *worker=*(mc_Filter **)m_Workers->GetRow(i); + err=pFilterEngine->RunFilter(worker,strResult); + if(err) + { + LogPrintf("Error while running filter %s, error: %d\n",m_Filters[i].m_FilterCaption.c_str(),err); + goto exitlbl; + } + if(strResult.size()) + { + if(lppFilter) + { + *lppFilter=&(m_Filters[i]); + } + strResult=strprintf("The transaction did not pass filter %s: %s",m_Filters[i].m_FilterCaption.c_str(),strResult.c_str()); + if(fDebug)LogPrint("filter","filter: %s\n",strResult.c_str()); + failure++; + goto exitlbl; + } + if(fDebug)LogPrint("filter","filter: Tx %s accepted, filter: %s\n",m_TxID.ToString().c_str(),m_Filters[i].m_FilterCaption.c_str()); + if(applied) + { + *applied+=1; + } + } + else + { + if(fDebug)LogPrint("filter","filter: Irrelevant, filter: %s\n",m_Filters[i].m_FilterCaption.c_str()); + } + } + } + } + +exitlbl: + if(fDebug)LogPrint("filter","filter: Applied filters: success - %d, failure - %d\n",*applied,failure); + + m_Params.Close(); + m_TxID=0; + return err; +} + +int mc_MultiChainFilterEngine::RunFilter(const CTransaction& tx,mc_Filter *filter,std::string &strResult) +{ + int err=MC_ERR_NOERROR; + m_Tx=tx; + m_TxID=m_Tx.GetHash(); + m_Params.Init(); + + err=pFilterEngine->RunFilter(filter,strResult); + + m_Params.Close(); + m_TxID=0; + return err; +} + +int mc_MultiChainFilterEngine::RunFilterWithCallbackLog(const CTransaction& tx,int vout,mc_Filter *filter,std::string &strResult, json_spirit::Array& callbacks) +{ + int err=MC_ERR_NOERROR; + m_Tx=tx; + m_TxID=m_Tx.GetHash(); + m_Params.Init(); + m_Vout=vout; + + err=pFilterEngine->RunFilterWithCallbackLog(filter,strResult, &callbacks); + + m_Params.Close(); + m_Vout=-1; + m_TxID=0; + return err; +} + +void mc_MultiChainFilterEngine::SetCallbackNames() +{ + m_CallbackNames.clear(); + + std::vector callbacks; // Tx filter callbacks + + callbacks.clear(); + callbacks.push_back("getfiltertxid"); + callbacks.push_back("getfiltertransaction"); + callbacks.push_back("setfilterparam"); + callbacks.push_back("getfiltertxinput"); + callbacks.push_back("getlastblockinfo"); + callbacks.push_back("getassetinfo"); + callbacks.push_back("getstreaminfo"); + callbacks.push_back("verifypermission"); + callbacks.push_back("verifymessage"); + + + if(mc_gState->m_NetworkParams->ProtocolVersion() >= 20005) + { + callbacks.push_back("getfilterassetbalances"); + } + + m_CallbackNames.push_back(callbacks); // Stream filters callbacks + + callbacks.clear(); + callbacks.push_back("getfiltertxid"); + callbacks.push_back("getfiltertransaction"); + callbacks.push_back("setfilterparam"); + callbacks.push_back("getlastblockinfo"); + callbacks.push_back("getassetinfo"); + callbacks.push_back("getstreaminfo"); + callbacks.push_back("verifypermission"); + callbacks.push_back("verifymessage"); + + if(mc_gState->m_NetworkParams->ProtocolVersion() >= 20005) + { + callbacks.push_back("getfilterstreamitem"); + } + + m_CallbackNames.push_back(callbacks); +} + +int mc_MultiChainFilterEngine::Initialize() +{ + mc_Buffer *filters; + unsigned char *txid; + int err=MC_ERR_NOERROR; + mc_EntityDetails entity; + map filter_refs; + uint64_t max_ref=0; + + m_Workers=new mc_Buffer; + m_Workers->Initialize(sizeof(mc_Filter*),sizeof(mc_Filter*),MC_BUF_MODE_DEFAULT); + + SetCallbackNames(); + + filters=NULL; + filters=mc_gState->m_Assets->GetEntityList(filters,NULL,MC_ENT_TYPE_FILTER); + + for(int i=0;iGetCount();i++) + { + txid=filters->GetRow(i); + if(mc_gState->m_Assets->FindEntityByTxID(&entity,txid)) + { + if(entity.IsUnconfirmedGenesis() == 0) + { + unsigned char *ptr; + ptr=(unsigned char *)entity.GetRef(); + uint64_t ref=(mc_GetLE(ptr,4) << 32) + mc_GetLE(ptr+4,4); + if(ref > max_ref) + { + max_ref=ref; + } + filter_refs.insert(pair(ref,*(uint256*)txid)); + } + } + } + + for(int i=0;iGetCount();i++) + { + txid=filters->GetRow(i); + if(mc_gState->m_Assets->FindEntityByTxID(&entity,txid)) + { + if(entity.IsUnconfirmedGenesis()) + { + max_ref++; + filter_refs.insert(pair(max_ref,*(uint256*)txid)); + } + } + } + + map::iterator it; + + for(it=filter_refs.begin();it != filter_refs.end();it++) + { + err=Add((unsigned char*)&(it->second)+MC_AST_SHORT_TXID_OFFSET,0); + if(err) + { + goto exitlbl; + } + } + + LogPrintf("Filter initialization completed\n"); + +exitlbl: + + mc_gState->m_Assets->FreeEntityList(filters); + + return MC_ERR_NOERROR; +} diff --git a/src/filters/multichainfilter.h b/src/filters/multichainfilter.h new file mode 100644 index 00000000..e7afebed --- /dev/null +++ b/src/filters/multichainfilter.h @@ -0,0 +1,116 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef MULTICHAINFILTER_H +#define MULTICHAINFILTER_H + +#include "core/init.h" +#include "core/main.h" +#include "utils/util.h" +#include "utils/utilparse.h" +#include "multichain/multichain.h" +#include "json/json_spirit_value.h" +//#include "filters/filter.h" + +#define MC_FLT_TYPE_BAD 0xFFFFFFFF +#define MC_FLT_TYPE_TX 0 +#define MC_FLT_TYPE_STREAM 1 + +#define MC_FLT_MAIN_NAME_TX "filtertransaction" +#define MC_FLT_MAIN_NAME_STREAM "filterstreamitem" + +std::vector mc_FillRelevantFilterEntitities(const unsigned char *ptr, size_t value_size); + +class mc_Filter; + +typedef struct mc_MultiChainFilter +{ + std::vector m_RelevantEntities; + + mc_EntityDetails m_Details; + std::string m_CreateError; + std::string m_MainName; + std::string m_FilterCaption; + char m_FilterCode[MC_ENT_MAX_SCRIPT_SIZE+1]; + uint32_t m_FilterType; + uint160 m_FilterAddress; + + mc_MultiChainFilter() + { + Zero(); + } + + ~mc_MultiChainFilter() + { + Destroy(); + } + + int Initialize(const unsigned char* short_txid); + + int Zero(); + int Destroy(); + + bool HasRelevantEntity(std::set & sRelevantEntities); + +} mc_MultiChainFilter; + +typedef struct mc_MultiChainFilterParams +{ + int m_MaxShownData; + int m_Compatibility; + mc_MultiChainFilterParams() + { + Zero(); + } + + ~mc_MultiChainFilterParams() + { + Destroy(); + } + + int Zero(); + int Destroy(); + int Init(); + int Close(); +}mc_MultiChainFilterParams; + +typedef struct mc_MultiChainFilterEngine +{ + std::vector m_Filters; + std::vector > m_CallbackNames; + mc_Buffer *m_Workers; + uint256 m_TxID; + CTransaction m_Tx; + int m_Vout; + mc_MultiChainFilterParams m_Params; + + mc_MultiChainFilterEngine() + { + Zero(); + } + + ~mc_MultiChainFilterEngine() + { + Destroy(); + } + + int Initialize(); + void SetCallbackNames(); + int GetAcceptTimeout(); + int GetSendTimeout(); + int SetTimeout(int timeout); + int Add(const unsigned char* short_txid,int for_block); + int Reset(int block,int for_block); + int RunTxFilters(const CTransaction& tx,std::set & sRelevantEntities,std::string &strResult,mc_MultiChainFilter **lppFilter,int *applied); + int RunStreamFilters(const CTransaction& tx,int vout, unsigned char *stream_short_txid,int block,int offset,std::string &strResult,mc_MultiChainFilter **lppFilter,int *applied); + int RunFilter(const CTransaction& tx,mc_Filter *filter,std::string &strResult); + int RunFilterWithCallbackLog(const CTransaction& tx,int vout,mc_Filter *filter,std::string &strResult, json_spirit::Array& callbacks); + int NoStreamFilters(); + + int Zero(); + int Destroy(); + +} mc_MultiChainFilterEngine; + +#endif /* MULTICHAINFILTER_H */ + diff --git a/src/filters/watchdog.cpp b/src/filters/watchdog.cpp new file mode 100644 index 00000000..b2a2895c --- /dev/null +++ b/src/filters/watchdog.cpp @@ -0,0 +1,176 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "watchdog.h" +#include "core/init.h" +//#include "protocol/filter.h" +#include "utils/define.h" +#include "utils/util.h" + +void WatchdogState::Set(WatchdogState::State state) +{ + if (m_state != state) + { + { + boost::lock_guard lock(m_mutex); + m_state = state; + } + m_condVar.notify_all(); + } +} + +std::string WatchdogState::Str() const +{ + std::string stateStr; + switch (m_state) + { + case State::INIT: + stateStr = "INIT"; + break; + case State::IDLE: + stateStr = "IDLE"; + break; + case State::RUNNING: + stateStr = "RUNNING"; + break; + case State::POISON_PILL: + stateStr = "POISON_PILL"; + break; + } + return tfm::format("%s %s", m_name, stateStr); +} + +void WatchdogState::WaitState(WatchdogState::State state) +{ + if (m_state != state) + { + boost::unique_lock lock(m_mutex); + m_condVar.wait(lock, [this, state] { return m_state == state; }); + } +} + +void WatchdogState::WaitNotState(WatchdogState::State state) +{ + if (m_state == state) + { + boost::unique_lock lock(m_mutex); + m_condVar.wait(lock, [this, state] { return m_state != state; }); + } +} + +template +bool WatchdogState::WaitStateFor(WatchdogState::State state, const boost::chrono::duration &timeout) +{ + boost::unique_lock lock(m_mutex); + return m_condVar.wait_for(lock, timeout, [this, state] { return m_state == state; }); +} + +template +bool WatchdogState::WaitNotStateFor(WatchdogState::State state, const boost::chrono::duration &timeout) +{ + boost::unique_lock lock(m_mutex); + return m_condVar.wait_for(lock, timeout, [this, state] { return m_state != state; }); +} + +void Watchdog::Watchdog::Zero() +{ + m_thread = nullptr; + m_timeout = 0; +} + +int Watchdog::Destroy() +{ + if (m_thread != nullptr) + { + m_thread->interrupt(); + m_thread->join(); + delete m_thread; + } + this->Zero(); + return MC_ERR_NOERROR; +} + +void Watchdog::FilterStarted(int timeout) +{ + if (fDebug) + LogPrint("", ": Watchdog::FilterStarted(timeout=%d) %s\n", timeout, m_actualState.Str()); + if (m_thread == nullptr) + { + if (fDebug) + LogPrint("", ": Watchdog::FilterStarted create thread with watchdogTask\n"); + m_thread = new boost::thread(std::bind(&Watchdog::WatchdogTask, this)); + } + m_actualState.WaitState(WatchdogState::State::IDLE); + m_timeout = timeout; + m_requestedState.Set(WatchdogState::State::RUNNING); + m_actualState.WaitState(WatchdogState::State::RUNNING); +} + +void Watchdog::FilterEnded() +{ + if (fDebug) + LogPrint("", ": Watchdog::FilterEnded %s\n", m_actualState.Str()); + m_actualState.WaitState(WatchdogState::State::RUNNING); + m_requestedState.Set(WatchdogState::State::IDLE); + m_actualState.WaitState(WatchdogState::State::IDLE); +} + +void Watchdog::Shutdown() +{ + if (fDebug) + LogPrint("", ": Watchdog::Shutdown\n"); + m_requestedState.Set(WatchdogState::State::POISON_PILL); + m_actualState.WaitState(WatchdogState::State::POISON_PILL); + this->Destroy(); +} + +void Watchdog::WatchdogTask() +{ + if (fDebug) + LogPrint("", ": Watchdog::watchdogTask\n"); + while (true) + { + std::string msg = tfm::format(": Watchdog::watchdogTask %s - %%s", m_requestedState.Str()); + switch (m_requestedState.Get()) + { + case WatchdogState::State::POISON_PILL: + if (fDebug) + LogPrint("", msg.c_str(), "committing suicide\n"); + m_actualState.Set(WatchdogState::State::POISON_PILL); + return; + + case WatchdogState::State::RUNNING: + m_actualState.Set(WatchdogState::State::RUNNING); + if (m_timeout > 0) + { + if (fDebug) + LogPrint("", msg.c_str(), "entering timed wait\n"); + bool finished = m_requestedState.WaitNotStateFor(WatchdogState::State::RUNNING, + boost::chrono::milliseconds(m_timeout)); + if (!finished) + { + if (fDebug) + LogPrint("", msg.c_str(), "timeout -> terminating filter\n"); + m_taskTerminator(tfm::format("Filter aborted due to timeout after %d ms", m_timeout).c_str()); + m_requestedState.WaitNotState(WatchdogState::State::RUNNING); + } + } + else + { + if (fDebug) + LogPrint("", msg.c_str(), "entering inifinte wait\n"); + m_requestedState.WaitNotState(WatchdogState::State::RUNNING); + } + break; + + case WatchdogState::State::INIT: + // fall through + case WatchdogState::State::IDLE: + if (fDebug) + LogPrint("", msg.c_str(), "entering idle state\n"); + m_actualState.Set(WatchdogState::State::IDLE); + m_requestedState.WaitNotState(WatchdogState::State::IDLE); + break; + } + } +} diff --git a/src/filters/watchdog.h b/src/filters/watchdog.h new file mode 100644 index 00000000..da69d71b --- /dev/null +++ b/src/filters/watchdog.h @@ -0,0 +1,87 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef WATCHDOG_H +#define WATCHDOG_H + +#include + +class WatchdogState +{ + public: + enum State + { + INIT, + IDLE, + RUNNING, + POISON_PILL + }; + + WatchdogState(std::string name, State state = State::INIT) : m_name(name), m_state(state) + { + } + + State Get() const + { + return m_state; + } + void Set(State state); + std::string Str() const; + + void WaitState(State state); + void WaitNotState(State state); + template + bool WaitStateFor(State state, const boost::chrono::duration &timeout); + template + bool WaitNotStateFor(State state, const boost::chrono::duration &timeout); + + protected: + std::string m_name; + boost::condition_variable m_condVar; + boost::mutex m_mutex; + State m_state; +}; + +class Watchdog +{ + public: + Watchdog(std::function taskTerminator) : m_taskTerminator(taskTerminator) + { + this->Zero(); + } + + ~Watchdog() + { + this->Destroy(); + } + + void Zero(); + int Destroy(); + + /** + * @brief Notfies the watchdog that a filter started runnug, with a given timeout. + * @param timeout The number of millisecond to allow the filtr to run. + */ + void FilterStarted(int timeout); + + /** + * @brief Notfies the watchdog that a filter stopped running. + */ + void FilterEnded(); + + /** + * @brief Terminate the watchdog. + */ + void Shutdown(); + + private: + boost::thread *m_thread; + int m_timeout; + WatchdogState m_requestedState{"requested"}; + WatchdogState m_actualState{"actual"}; + std::function m_taskTerminator; + + void WatchdogTask(); +}; + +#endif // V8FILTERWATCHDOG_H diff --git a/src/json/json_spirit_ubjson.cpp b/src/json/json_spirit_ubjson.cpp index 04a2f941..7faee3a2 100644 --- a/src/json/json_spirit_ubjson.cpp +++ b/src/json/json_spirit_ubjson.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "json/json_spirit_ubjson.h" diff --git a/src/json/json_spirit_ubjson.h b/src/json/json_spirit_ubjson.h index 0b5939f8..92dd88c1 100644 --- a/src/json/json_spirit_ubjson.h +++ b/src/json/json_spirit_ubjson.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef JSON_SPIRIT_UBJSON_H diff --git a/src/json/json_spirit_writer_template.h b/src/json/json_spirit_writer_template.h index 21bc0920..3700d666 100644 --- a/src/json/json_spirit_writer_template.h +++ b/src/json/json_spirit_writer_template.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include extern uint32_t JSON_NO_DOUBLE_FORMATTING; extern int JSON_DOUBLE_DECIMAL_DIGITS; diff --git a/src/keys/key.cpp b/src/keys/key.cpp index ae35f9a6..5d2868b5 100644 --- a/src/keys/key.cpp +++ b/src/keys/key.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2016 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "keys/key.h" diff --git a/src/keys/key.h b/src/keys/key.h index 2a120386..77f59d88 100644 --- a/src/keys/key.h +++ b/src/keys/key.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_KEY_H diff --git a/src/keys/pubkey.cpp b/src/keys/pubkey.cpp index 77e3e76d..1a0a4cfe 100644 --- a/src/keys/pubkey.cpp +++ b/src/keys/pubkey.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2016 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "keys/pubkey.h" diff --git a/src/keys/pubkey.h b/src/keys/pubkey.h index 9d8123a0..126db894 100644 --- a/src/keys/pubkey.h +++ b/src/keys/pubkey.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_PUBKEY_H diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc index 49b95953..72c39354 100644 --- a/src/leveldb/db/db_impl.cc +++ b/src/leveldb/db/db_impl.cc @@ -1,3 +1,5 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. @@ -1107,7 +1109,7 @@ Status DBImpl::Get(const ReadOptions& options, // Done } else { s = current->Get(options, lkey, value, &stats); - have_stat_update = true; +// have_stat_update = true; } mutex_.Lock(); } diff --git a/src/miner/miner.cpp b/src/miner/miner.cpp index 6c0ae8a8..8dd036bd 100644 --- a/src/miner/miner.cpp +++ b/src/miner/miner.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "miner/miner.h" @@ -112,6 +112,21 @@ bool UpdateTime(CBlockHeader* pblock, const CBlockIndex* pindexPrev) bool CreateBlockSignature(CBlock *block,uint32_t hash_type,CWallet *pwallet) { + if(Params().DisallowUnsignedBlockNonce()) + { + if(hash_type != BLOCKSIGHASH_NO_SIGNATURE) + { + return true; + } + } + else + { + if(hash_type != BLOCKSIGHASH_NO_SIGNATURE_AND_NONCE) + { + return true; + } + } + int coinbase_tx,op_return_output; uint256 hash_to_verify; uint256 original_merkle_root; @@ -121,6 +136,7 @@ bool CreateBlockSignature(CBlock *block,uint32_t hash_type,CWallet *pwallet) block->nMerkleTreeType=MERKLETREE_FULL; block->nSigHashType=BLOCKSIGHASH_NONE; + if(!mc_gState->m_NetworkParams->IsProtocolMultichain()) { block->hashMerkleRoot=block->BuildMerkleTree(); @@ -189,9 +205,13 @@ bool CreateBlockSignature(CBlock *block,uint32_t hash_type,CWallet *pwallet) hash_to_verify=block->GetHash(); break; case BLOCKSIGHASH_NO_SIGNATURE_AND_NONCE: + case BLOCKSIGHASH_NO_SIGNATURE: block->nMerkleTreeType=MERKLETREE_NO_COINBASE_OP_RETURN; block->hashMerkleRoot=block->BuildMerkleTree(); - block->nNonce=0; + if(hash_type == BLOCKSIGHASH_NO_SIGNATURE_AND_NONCE) + { + block->nNonce=0; + } hash_to_verify=block->GetHash(); block->nMerkleTreeType=MERKLETREE_FULL; break; @@ -249,6 +269,7 @@ bool CreateBlockSignature(CBlock *block,uint32_t hash_type,CWallet *pwallet) switch(hash_type) { case BLOCKSIGHASH_NO_SIGNATURE_AND_NONCE: + case BLOCKSIGHASH_NO_SIGNATURE: block->hashMerkleRoot=block->BuildMerkleTree(); break; } @@ -633,8 +654,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn,CWallet *pwallet,CP { if(canMine) { - const unsigned char *pubkey_hash=(unsigned char *)Hash160(ppubkey->begin(),ppubkey->end()).begin(); - *canMine=mc_gState->m_Permissions->CanMine(NULL,pubkey_hash); +// const unsigned char *pubkey_hash=(unsigned char *)Hash160(ppubkey->begin(),ppubkey->end()).begin(); +// *canMine=mc_gState->m_Permissions->CanMine(NULL,pubkey_hash); + uint160 pubkey_hash=Hash160(ppubkey->begin(),ppubkey->end()); + *canMine=mc_gState->m_Permissions->CanMine(NULL,&pubkey_hash); if((*canMine & MC_PTP_MINE) == 0) { if(prevCanMine & MC_PTP_MINE) @@ -724,20 +747,30 @@ int64_t nHPSTimerStart = 0; // nonce is 0xffff0000 or above, the block is rebuilt and nNonce starts over at // zero. // -bool static ScanHash(const CBlockHeader *pblock, uint32_t& nNonce, uint256 *phash,uint16_t success_and_mask) +bool static ScanHash(CBlock *pblock, uint32_t& nNonce, uint256 *phash,uint16_t success_and_mask,CWallet *pwallet) { // Write the first 76 bytes of the block header to a double-SHA256 state. CHash256 hasher; CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << *pblock; + ss << pblock->GetBlockHeader(); +// ss << *pblock; assert(ss.size() == 80); hasher.Write((unsigned char*)&ss[0], 76); while (true) { nNonce++; - // Write the last 4 bytes of the block header (the nonce) to a copy of - // the double-SHA256 state, and compute the result. - CHash256(hasher).Write((unsigned char*)&nNonce, 4).Finalize((unsigned char*)phash); + if(Params().DisallowUnsignedBlockNonce()) + { + pblock->nNonce=nNonce; + CreateBlockSignature(pblock,BLOCKSIGHASH_NO_SIGNATURE,pwallet); + *phash=pblock->GetHash(); + } + else + { + // Write the last 4 bytes of the block header (the nonce) to a copy of + // the double-SHA256 state, and compute the result. + CHash256(hasher).Write((unsigned char*)&nNonce, 4).Finalize((unsigned char*)phash); + } // Return the nonce if the hash has at least some zero bits, // caller will check if it has enough to reach the target @@ -795,7 +828,11 @@ CBlockTemplate* CreateNewBlockWithDefaultKey(CWallet *pwallet,int *canMine,const return NULL; } - const unsigned char *pubkey_hash=(unsigned char *)Hash160(pubkey.begin(),pubkey.end()).begin(); +// const unsigned char *pubkey_hash=(unsigned char *)Hash160(pubkey.begin(),pubkey.end()).begin(); + + unsigned char pubkey_hash[20]; + uint160 pkhash=Hash160(pubkey.begin(),pubkey.end()); + memcpy(pubkey_hash,&pkhash,20); CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << vector(pubkey_hash, pubkey_hash + 20) << OP_EQUALVERIFY << OP_CHECKSIG; @@ -1423,7 +1460,10 @@ void static BitcoinMiner(CWallet *pwallet) if(canMine & MC_PTP_MINE) { - const unsigned char *pubkey_hash=(unsigned char *)Hash160(kMiner.begin(),kMiner.end()).begin(); +// const unsigned char *pubkey_hash=(unsigned char *)Hash160(kMiner.begin(),kMiner.end()).begin(); + unsigned char pubkey_hash[20]; + uint160 pkhash=Hash160(kMiner.begin(),kMiner.end()); + memcpy(pubkey_hash,&pkhash,20); CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << vector(pubkey_hash, pubkey_hash + 20) << OP_EQUALVERIFY << OP_CHECKSIG; canMine=prevCanMine; auto_ptr pblocktemplate(CreateNewBlock(scriptPubKey,pwallet,&kMiner,&canMine,&pindexPrev)); @@ -1461,8 +1501,7 @@ void static BitcoinMiner(CWallet *pwallet) break; } } - - bool fFound = ScanHash(pblock, nNonce, &hash, success_and_mask); + bool fFound = ScanHash(pblock, nNonce, &hash, success_and_mask, pwallet); uint32_t nHashesDone = nNonce - nOldNonce; nOldNonce = nNonce; diff --git a/src/miner/miner.h b/src/miner/miner.h index fff4a3bd..95d970e3 100644 --- a/src/miner/miner.h +++ b/src/miner/miner.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_MINER_H @@ -31,6 +31,7 @@ CBlockTemplate* CreateNewBlockWithDefaultKey(CWallet *pwallet,int *canMine, cons /** Modify the extranonce in a block */ /* MCHN START */ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce,CWallet *pwallet); +bool CreateBlockSignature(CBlock *block,uint32_t hash_type,CWallet *pwallet); //void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); /* MCHN END */ /** Check mined block */ diff --git a/src/multichain-cli-res.rc b/src/multichain-cli-res.rc index 173d3d13..827b582d 100644 --- a/src/multichain-cli-res.rc +++ b/src/multichain-cli-res.rc @@ -18,13 +18,13 @@ BEGIN BEGIN VALUE "CompanyName", "Coin Sciences Ltd" VALUE "FileDescription", "multichain-cli (OSS RPC client for MultiChain)" - VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "FileVersion", WIN_VERSION_STR VALUE "InternalName", "multichain-cli" VALUE "LegalCopyright", COPYRIGHT_STR VALUE "LegalTrademarks1", "The software product under this license is provided free of charge. Full terms are shown at: http://www.multichain.com/terms-of-service/." VALUE "OriginalFilename", "multichain-cli.exe" VALUE "ProductName", "multichain-cli" - VALUE "ProductVersion", VER_PRODUCTVERSION_STR + VALUE "ProductVersion", WIN_VERSION_STR END END diff --git a/src/multichain-util-res.rc b/src/multichain-util-res.rc index 09b285f0..a023fd42 100644 --- a/src/multichain-util-res.rc +++ b/src/multichain-util-res.rc @@ -18,13 +18,13 @@ BEGIN BEGIN VALUE "CompanyName", "Coin Sciences Ltd" VALUE "FileDescription", "multichain-util (Utilities for MultiChain)" - VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "FileVersion", WIN_VERSION_STR VALUE "InternalName", "multichain-util" VALUE "LegalCopyright", COPYRIGHT_STR VALUE "LegalTrademarks1", "The software product under this license is provided free of charge. Full terms are shown at: http://www.multichain.com/terms-of-service/." VALUE "OriginalFilename", "multichain-util.exe" VALUE "ProductName", "multichain-util" - VALUE "ProductVersion", VER_PRODUCTVERSION_STR + VALUE "ProductVersion", WIN_VERSION_STR END END diff --git a/src/multichain/multichain-cli.cpp b/src/multichain/multichain-cli.cpp index 64c35a6b..be84f1e1 100644 --- a/src/multichain/multichain-cli.cpp +++ b/src/multichain/multichain-cli.cpp @@ -1,12 +1,13 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "chainparams/chainparamsbase.h" #include "version/clientversion.h" #include "rpc/rpcclient.h" #include "rpc/rpcprotocol.h" +#include "rpc/rpcasio.h" #include "utils/util.h" #include "utils/utilstrencodings.h" @@ -411,6 +412,19 @@ int main(int argc, char* argv[]) boost::filesystem::create_directories(path_cli_log); path_cli_log /= string(mc_gState->m_Params->NetworkName() + string(".log")); + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + { + string strMethod=strprintf("%s",mc_gState->m_Params->NetworkName()); + if(HaveAPIWithThisName(strMethod)) + { + fprintf(stdout,"\nMultiChain %s RPC client\n\n",mc_BuildDescription(mc_gState->GetNumericVersion()).c_str()); + printf("ERROR: Couldn't read configuration file for blockchain %s. \n\n" + "Be sure include the blockchain name before the command name, e.g.:\n\n" + "multichain-cli chain1 %s\n\n",mc_gState->m_Params->NetworkName(),mc_gState->m_Params->NetworkName()); + return EXIT_FAILURE; + } + } + #ifndef WIN32 if(mc_gState->m_Params->m_NumArguments == 1) // Interactive mode { @@ -517,7 +531,7 @@ int main(int argc, char* argv[]) return 0; } #endif - + int ret = EXIT_FAILURE; try { mc_SaveCliCommandToLog(path_cli_log.string().c_str(), argc, argv); diff --git a/src/multichain/multichain-util.cpp b/src/multichain/multichain-util.cpp index 06879e68..ae6fc2b8 100644 --- a/src/multichain/multichain-util.cpp +++ b/src/multichain/multichain-util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "multichain/multichain.h" diff --git a/src/multichain/multichain.h b/src/multichain/multichain.h index 72be1973..776b1865 100644 --- a/src/multichain/multichain.h +++ b/src/multichain/multichain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAIN_H diff --git a/src/multichain/multichaind-cold.cpp b/src/multichain/multichaind-cold.cpp index cc3a01ee..1f3e8a94 100644 --- a/src/multichain/multichaind-cold.cpp +++ b/src/multichain/multichaind-cold.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "version/clientversion.h" @@ -19,6 +19,7 @@ #include "multichain/multichain.h" #include "chainparams/globals.h" static bool fDaemon; +mc_EnterpriseFeatures* pEF = NULL; void DebugPrintClose(); std::string HelpMessage_Cold(); @@ -54,6 +55,19 @@ bool mc_DoesParentDataDirExist() return true; } +bool mc_DoesParentLogDirExist() +{ + if (mapArgs.count("-logdir")) + { + boost::filesystem::path path=boost::filesystem::system_complete(mapArgs["-logdir"]); + if (!boost::filesystem::is_directory(path)) + { + return false; + } + } + return true; +} + ////////////////////////////////////////////////////////////////////////////// // // Start @@ -102,6 +116,12 @@ bool AppInit(int argc, char* argv[]) return false; } + if(!mc_DoesParentLogDirExist()) + { + fprintf(stderr,"\nError: Log directory %s needs to exist before calling multichaind-cold. Exiting...\n\n",mapArgs["-logdir"].c_str()); + return false; + } + mc_gState->m_Params->HasOption("-?"); @@ -234,7 +254,7 @@ bool AppInit(int argc, char* argv[]) { char fileName[MC_DCT_DB_MAX_PATH]; mc_GetFullFileName(mc_gState->m_Params->NetworkName(),"params", ".dat",MC_FOM_RELATIVE_TO_DATADIR,fileName); - fprintf(stderr,"ERROR: Parameter set for blockchain %s is not valid.\n\nThe file %s must be copied manually from an existing node.\n\n", + fprintf(stderr,"ERROR: Parameter set for blockchain %s is not valid.\n\nThe file %s must be copied manually from an existing node into empty blockchain directory.\n\n", mc_gState->m_Params->NetworkName(),fileName); delete mc_gState; return false; diff --git a/src/multichain/multichaind.cpp b/src/multichain/multichaind.cpp index c9560b10..20771708 100644 --- a/src/multichain/multichaind.cpp +++ b/src/multichain/multichaind.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "version/clientversion.h" @@ -10,6 +10,7 @@ #include "ui/noui.h" #include "ui/ui_interface.h" #include "utils/util.h" +#include "community/community.h" #include @@ -20,6 +21,8 @@ #include "chainparams/globals.h" static bool fDaemon; +mc_EnterpriseFeatures* pEF = NULL; + void DebugPrintClose(); void DetectShutdownThread(boost::thread_group* threadGroup) @@ -51,6 +54,20 @@ bool mc_DoesParentDataDirExist() return true; } +bool mc_DoesParentLogDirExist() +{ + if (mapArgs.count("-logdir")) + { + boost::filesystem::path path=boost::filesystem::system_complete(mapArgs["-logdir"]); + if (!boost::filesystem::is_directory(path)) + { + return false; + } + } + return true; +} + + ////////////////////////////////////////////////////////////////////////////// // // Start @@ -90,15 +107,37 @@ bool AppInit(int argc, char* argv[]) return false; } } - + if(!mc_DoesParentDataDirExist()) { fprintf(stderr,"\nError: Data directory %s needs to exist before calling multichaind. Exiting...\n\n",mapArgs["-datadir"].c_str()); + delete mc_gState; return false; } + if(!mc_DoesParentLogDirExist()) + { + fprintf(stderr,"\nError: Log directory %s needs to exist before calling multichaind. Exiting...\n\n",mapArgs["-logdir"].c_str()); + return false; + } + + pEF=new mc_EnterpriseFeatures; +/* + if(pEF->Initialize(mc_gState->m_Params->NetworkName(),0)) + { + fprintf(stderr,"\nError: Data directory %s needs to exist before calling multichaind. Exiting...\n\n",mapArgs["-datadir"].c_str()); + delete mc_gState; + return false; + } +*/ + string edition=pEF->ENT_Edition(); + if(edition.size()) + { + edition=edition+" Edition, "; + } + mc_gState->m_Params->HasOption("-?"); @@ -108,7 +147,7 @@ bool AppInit(int argc, char* argv[]) mc_gState->m_Params->HasOption("-version") || (mc_gState->m_Params->NetworkName() == NULL)) { - fprintf(stdout,"\nMultiChain %s Daemon (protocol %s)\n\n",mc_BuildDescription(mc_gState->GetNumericVersion()).c_str(),mc_SupportedProtocols().c_str()); + fprintf(stdout,"\nMultiChain %s Daemon (%sprotocol %s)\n\n",mc_BuildDescription(mc_gState->GetNumericVersion()).c_str(),edition.c_str(),mc_SupportedProtocols().c_str()); std::string strUsage = ""; if (mc_gState->m_Params->HasOption("-version")) { @@ -124,13 +163,14 @@ bool AppInit(int argc, char* argv[]) fprintf(stdout, "%s", strUsage.c_str()); + delete pEF; delete mc_gState; return true; } if(!GetBoolArg("-shortoutput", false)) { - fprintf(stdout,"\nMultiChain %s Daemon (latest protocol %d)\n\n",mc_BuildDescription(mc_gState->GetNumericVersion()).c_str(),mc_gState->GetProtocolVersion()); + fprintf(stdout,"\nMultiChain %s Daemon (%slatest protocol %d)\n\n",mc_BuildDescription(mc_gState->GetNumericVersion()).c_str(),edition.c_str(),mc_gState->GetProtocolVersion()); } pipes[1]=STDOUT_FILENO; @@ -140,6 +180,7 @@ bool AppInit(int argc, char* argv[]) if (fDaemon) { + delete pEF; delete mc_gState; if(!GetBoolArg("-shortoutput", false)) @@ -196,6 +237,8 @@ bool AppInit(int argc, char* argv[]) mc_gState->m_Params->Parse(argc, argv, MC_ETP_DAEMON); mc_CheckDataDirInConfFile(); + + pEF=new mc_EnterpriseFeatures; } #endif @@ -206,6 +249,7 @@ bool AppInit(int argc, char* argv[]) if(err) { fprintf(stderr,"ERROR: Couldn't read parameter file for blockchain %s. Exiting...\n",mc_gState->m_Params->NetworkName()); + delete pEF; delete mc_gState; return false; } @@ -214,6 +258,7 @@ bool AppInit(int argc, char* argv[]) if(err) { fprintf(stderr,"ERROR: Couldn't read configuration file for blockchain %s. Please try upgrading MultiChain. Exiting...\n",mc_gState->m_Params->NetworkName()); + delete pEF; delete mc_gState; return false; } @@ -222,6 +267,7 @@ bool AppInit(int argc, char* argv[]) if(err) { fprintf(stderr,"ERROR: Couldn't validate parameter set for blockchain %s. Exiting...\n",mc_gState->m_Params->NetworkName()); + delete pEF; delete mc_gState; return false; } @@ -245,6 +291,7 @@ bool AppInit(int argc, char* argv[]) { fprintf(stderr,"\nERROR: Couldn't initialize permission database for blockchain %s. Probably multichaind for this blockchain is already running. Exiting...\n",mc_gState->m_Params->NetworkName()); } + delete pEF; delete mc_gState; return false; } @@ -260,6 +307,7 @@ bool AppInit(int argc, char* argv[]) fprintf(stderr,"ERROR: The protocol version (%d) for blockchain %s has been deprecated and was last supported in MultiChain %s\n\n", protocol_version,mc_gState->m_Params->NetworkName(), mc_BuildDescription(-mc_gState->VersionInfo(protocol_version)).c_str()); + delete pEF; delete mc_gState; return false; } @@ -268,6 +316,7 @@ bool AppInit(int argc, char* argv[]) fprintf(stderr,"ERROR: Parameter set for blockchain %s was generated by MultiChain running newer protocol version (%d)\n\n", mc_gState->m_Params->NetworkName(),protocol_version); fprintf(stderr,"Please upgrade MultiChain\n\n"); + delete pEF; delete mc_gState; return false; } @@ -297,6 +346,7 @@ bool AppInit(int argc, char* argv[]) fprintf(stderr,"If you want to connect to existing blockchain please specify seed node:\n\n"); fprintf(stderr," multichaind %s@\n",mc_gState->m_Params->NetworkName()); fprintf(stderr," multichaind %s@:\n\n\n",mc_gState->m_Params->NetworkName()); + delete pEF; delete mc_gState; return false; } @@ -306,11 +356,13 @@ bool AppInit(int argc, char* argv[]) fprintf(stderr," multichain-util create %s\n",mc_gState->m_Params->NetworkName()); fprintf(stderr," multichain-util clone %s\n",mc_gState->m_Params->NetworkName()); fprintf(stderr,"\nAnd rerun multichaind %s\n",mc_gState->m_Params->NetworkName()); + delete pEF; delete mc_gState; return false; case MC_PRM_STATUS_INVALID: fprintf(stderr,"ERROR: Parameter set for blockchain %s is invalid. You may generate new network using these parameters by running:\n\n",mc_gState->m_Params->NetworkName()); fprintf(stderr," multichain-util clone %s \n",mc_gState->m_Params->NetworkName()); + delete pEF; delete mc_gState; return false; case MC_PRM_STATUS_GENERATED: @@ -318,6 +370,7 @@ bool AppInit(int argc, char* argv[]) break; default: fprintf(stderr,"INTERNAL ERROR: Unknown parameter set status %d\n",mc_gState->m_NetworkParams->m_Status); + delete pEF; delete mc_gState; return false; break; @@ -399,6 +452,7 @@ bool AppInit(int argc, char* argv[]) } } + delete pEF; delete mc_gState; if(datadir_to_delete.size()) diff --git a/src/multichaind-cold-res.rc b/src/multichaind-cold-res.rc index a2a959fd..10c6e613 100644 --- a/src/multichaind-cold-res.rc +++ b/src/multichaind-cold-res.rc @@ -18,13 +18,13 @@ BEGIN BEGIN VALUE "CompanyName", "Coin Sciences Ltd" VALUE "FileDescription", "multichaind-cold (OSS daemon/client for MultiChain)" - VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "FileVersion", WIN_VERSION_STR VALUE "InternalName", "multichaind-cold" VALUE "LegalCopyright", COPYRIGHT_STR VALUE "LegalTrademarks1", "The software product under this license is provided free of charge. Full terms are shown at: http://www.multichain.com/terms-of-service/." VALUE "OriginalFilename", "multichaind-cold.exe" VALUE "ProductName", "multichaind-cold" - VALUE "ProductVersion", VER_PRODUCTVERSION_STR + VALUE "ProductVersion", WIN_VERSION_STR END END diff --git a/src/multichaind-res.rc b/src/multichaind-res.rc index 9e4b7dc2..e1858f9f 100644 --- a/src/multichaind-res.rc +++ b/src/multichaind-res.rc @@ -18,13 +18,13 @@ BEGIN BEGIN VALUE "CompanyName", "Coin Sciences Ltd" VALUE "FileDescription", "multichaind (OSS daemon/client for MultiChain)" - VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "FileVersion", WIN_VERSION_STR VALUE "InternalName", "multichaind" VALUE "LegalCopyright", COPYRIGHT_STR VALUE "LegalTrademarks1", "The software product under this license is provided free of charge. Full terms are shown at: http://www.multichain.com/terms-of-service/." VALUE "OriginalFilename", "multichaind.exe" VALUE "ProductName", "multichaind" - VALUE "ProductVersion", VER_PRODUCTVERSION_STR + VALUE "ProductVersion", WIN_VERSION_STR END END diff --git a/src/net/net.cpp b/src/net/net.cpp index 1ad98144..f3121460 100644 --- a/src/net/net.cpp +++ b/src/net/net.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #if defined(HAVE_CONFIG_H) @@ -79,6 +79,9 @@ bool fListen = true; uint64_t nLocalServices = NODE_NETWORK; CCriticalSection cs_mapLocalHost; map mapLocalHost; +CCriticalSection cs_setLocalAddr; +set setLocalAddr; + static bool vfReachable[NET_MAX] = {}; static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; @@ -325,6 +328,17 @@ bool SeenLocal(const CService& addr) return true; } +bool SeenLocalAddr(const CService& addr) +{ + { + LOCK(cs_setLocalAddr); + if (setLocalAddr.find(addr) == setLocalAddr.end()) + { + setLocalAddr.insert(addr); + } + } + return true; +} /** check whether a given address is potentially local */ bool IsLocal(const CService& addr) @@ -394,6 +408,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) { if (pszDest == NULL) { // if (IsLocal(addrConnect)) + if (IsLocal(addrConnect) && (addrConnect.GetPort() == GetListenPort())) return NULL; @@ -936,7 +951,7 @@ void ThreadSocketHandler() if (nErr != WSAEWOULDBLOCK) LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr)); } - else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS) + else if (nInbound >= nMaxConnections - GetArg("-maxoutconnections",MAX_OUTBOUND_CONNECTIONS)) { CloseSocket(hSocket); } @@ -1353,16 +1368,19 @@ void ThreadOpenConnections() // CAddress addrConnect; + // Only connect out to one peer per network group (/16 for IPv4). // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. int nOutbound = 0; + int nNodes = 0; set > setConnected; set setConnectedVerifiedAddresses; set setConnectedFromAddresses; set setConnectedToAddresses; { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + nNodes=(int)vNodes.size(); + BOOST_FOREACH(CNode* pnode, vNodes) { if (!pnode->fInbound) { setConnected.insert(pnode->addr.GetGroup()); nOutbound++; @@ -1386,11 +1404,13 @@ void ThreadOpenConnections() int64_t nANow = GetAdjustedTime(); + addrman.SCRecalculate(nANow); + int nTries = 0; while (true) { // use an nUnkBias between 10 (no outgoing connections) and 90 (8 outgoing connections) - CAddress addr = addrman.Select(10 + min(nOutbound,8)*10); + CAddress addr = addrman.Select(10 + min(nOutbound,8)*10,nNodes); // if we selected an invalid address, restart /* MCHN START */ @@ -1398,15 +1418,26 @@ void ThreadOpenConnections() { if (!addr.IsValid() || (IsLocal(addr) && (addr.GetPort() == GetListenPort()))) { + addrman.SetSC(true,nANow); break; } + + LOCK(cs_setLocalAddr); + if(setLocalAddr.find(addr) != setLocalAddr.end()) + { + addrman.SetSC(true,nANow); + break; + } MilliSleep(100); } else { /* MCHN END */ if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) + { + addrman.SetSC(true,nANow); break; + } /* MCHN START */ } /* MCHN END */ @@ -1427,7 +1458,9 @@ void ThreadOpenConnections() } if (IsLimited(addr)) + { continue; + } // only consider very recently tried nodes after 30 failed attempts /* MCHN START */ @@ -1435,7 +1468,9 @@ void ThreadOpenConnections() { /* MCHN END */ if (nANow - addr.nLastTry < 600 && nTries < 30) + { continue; + } /* MCHN START */ } /* MCHN END */ @@ -1459,16 +1494,19 @@ void ThreadOpenConnections() if(setConnectedVerifiedAddresses.count(addr.ToStringIPPort())) { + addrman.SetSC(false,nANow); continue; } if(setConnectedToAddresses.count(addr.ToStringIPPort())) { + addrman.SetSC(false,nANow); continue; } if (nANow - addr.nLastTry < 600) { if(setConnectedFromAddresses.count(addr.ToStringIPPort())) { + addrman.SetSC(false,nANow); continue; } } @@ -1486,6 +1524,7 @@ void ThreadOpenConnections() if (addrConnect.IsValid()) { OpenNetworkConnection(addrConnect, &grant); + addrman.SetSC(false,nANow); } } /* MCHN END */ @@ -1872,7 +1911,7 @@ void StartNode(boost::thread_group& threadGroup) if (semOutbound == NULL) { // initialize semaphore - int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections); + int nMaxOutbound = min((int)GetArg("-maxoutconnections",MAX_OUTBOUND_CONNECTIONS), nMaxConnections); semOutbound = new CSemaphore(nMaxOutbound); } @@ -1915,7 +1954,7 @@ bool StopNode() LogPrintf("StopNode()\n"); MapPort(false); if (semOutbound) - for (int i=0; ipost(); if (fAddressesInitialized) diff --git a/src/net/net.h b/src/net/net.h index 8ee38c54..b0b634b8 100644 --- a/src/net/net.h +++ b/src/net/net.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_NET_H @@ -117,6 +117,7 @@ bool IsLimited(const CNetAddr& addr); bool AddLocal(const CService& addr, int nScore = LOCAL_NONE); bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); bool SeenLocal(const CService& addr); +bool SeenLocalAddr(const CService& addr); bool IsLocal(const CService& addr); bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL); bool IsReachable(enum Network net); diff --git a/src/net/netbase.cpp b/src/net/netbase.cpp index 2dbfa403..1920b1c6 100644 --- a/src/net/netbase.cpp +++ b/src/net/netbase.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifdef HAVE_CONFIG_H diff --git a/src/net/netbase.h b/src/net/netbase.h index cea2037c..6c0af1b3 100644 --- a/src/net/netbase.h +++ b/src/net/netbase.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_NETBASE_H diff --git a/src/net/rest.cpp b/src/net/rest.cpp index 0ccef9aa..82bd1549 100644 --- a/src/net/rest.cpp +++ b/src/net/rest.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "primitives/block.h" diff --git a/src/permissions/permission.cpp b/src/permissions/permission.cpp index a8a97b95..8971839f 100644 --- a/src/permissions/permission.cpp +++ b/src/permissions/permission.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "multichain/multichain.h" @@ -292,6 +292,7 @@ int mc_Permissions::Zero() m_MempoolPermissions=NULL; m_MempoolPermissionsToReplay=NULL; m_CheckForMempoolFlag=0; + m_RollBackPos.Zero(); return MC_ERR_NOERROR; } @@ -327,7 +328,7 @@ int mc_Permissions::Initialize(const char *name,int mode) m_Ledger->SetName(name); m_Database->SetName(name); - mc_GetFullFileName(name,"permissions",".log",MC_FOM_RELATIVE_TO_DATADIR,m_LogFileName); + mc_GetFullFileName(name,"permissions",".log",MC_FOM_RELATIVE_TO_LOGDIR | MC_FOM_CREATE_DIR,m_LogFileName); err=m_Database->Open(); @@ -720,11 +721,23 @@ uint32_t mc_Permissions::GetPossiblePermissionTypes(const void* entity_details) { if(entity->GetEntityType()) { - return entity->Permissions(); + full_type = entity->Permissions(); + if(entity->GetEntityType() == MC_ENT_TYPE_ASSET) + { + if(mc_gState->m_Features->FixedIn20005()) + { + full_type |= MC_PTP_SEND | MC_PTP_RECEIVE; + } + } + full_type |= GetCustomLowPermissionTypes(); + full_type |= GetCustomHighPermissionTypes(); + return full_type; } } full_type = MC_PTP_GLOBAL_ALL; + full_type |= GetCustomLowPermissionTypes(); + full_type |= GetCustomHighPermissionTypes(); return full_type; } @@ -755,6 +768,24 @@ uint32_t mc_Permissions::GetPossiblePermissionTypes(uint32_t entity_type) return full_type; } +uint32_t mc_Permissions::GetCustomLowPermissionTypes() +{ + if(mc_gState->m_Features->CustomPermissions()) + { + return MC_PTP_CUSTOM1 | MC_PTP_CUSTOM2 | MC_PTP_CUSTOM3; + } + return MC_PTP_NONE; +} + +uint32_t mc_Permissions::GetCustomHighPermissionTypes() +{ + if(mc_gState->m_Features->CustomPermissions()) + { + return MC_PTP_CUSTOM4 | MC_PTP_CUSTOM5 | MC_PTP_CUSTOM6; + } + return MC_PTP_NONE; +} + /** Return ORed MC_PTP_ constants by textual value */ @@ -770,9 +801,7 @@ uint32_t mc_Permissions::GetPermissionType(const char *str,uint32_t full_type) char* start; char* ptrEnd; char c; - -// full_type=GetPossiblePermissionTypes(entity_details); - + ptr=(char*)str; ptrEnd=ptr+strlen(ptr); start=ptr; @@ -798,8 +827,14 @@ uint32_t mc_Permissions::GetPermissionType(const char *str,uint32_t full_type) if(mc_MemcmpCheckSize(start,"mine", ptr-start) == 0)perm_type = MC_PTP_MINE; if(mc_MemcmpCheckSize(start,"admin", ptr-start) == 0)perm_type = MC_PTP_ADMIN; if(mc_MemcmpCheckSize(start,"activate", ptr-start) == 0)perm_type = MC_PTP_ACTIVATE; - if(mc_MemcmpCheckSize(start,"create", ptr-start) == 0)perm_type = MC_PTP_CREATE; - if(mc_MemcmpCheckSize(start,"write", ptr-start) == 0)perm_type = MC_PTP_WRITE; + if(mc_MemcmpCheckSize(start,"create", ptr-start) == 0)perm_type = MC_PTP_CREATE; + if(mc_MemcmpCheckSize(start,"write", ptr-start) == 0)perm_type = MC_PTP_WRITE; + if(mc_MemcmpCheckSize(start,MC_PTN_CUSTOM1, ptr-start) == 0)perm_type = MC_PTP_CUSTOM1; + if(mc_MemcmpCheckSize(start,MC_PTN_CUSTOM2, ptr-start) == 0)perm_type = MC_PTP_CUSTOM2; + if(mc_MemcmpCheckSize(start,MC_PTN_CUSTOM3, ptr-start) == 0)perm_type = MC_PTP_CUSTOM3; + if(mc_MemcmpCheckSize(start,MC_PTN_CUSTOM4, ptr-start) == 0)perm_type = MC_PTP_CUSTOM4; + if(mc_MemcmpCheckSize(start,MC_PTN_CUSTOM5, ptr-start) == 0)perm_type = MC_PTP_CUSTOM5; + if(mc_MemcmpCheckSize(start,MC_PTN_CUSTOM6, ptr-start) == 0)perm_type = MC_PTP_CUSTOM6; if(perm_type == 0) { @@ -820,6 +855,84 @@ uint32_t mc_Permissions::GetPermissionType(const char *str,uint32_t full_type) return result; } +void mc_RollBackPos::Zero() +{ + m_Block=-1; + m_Offset=0; + m_InMempool=0; +} + + +int mc_RollBackPos::IsOut(int block,int offset) +{ + if(block == m_Block) + { + return (offset > m_Offset) ? 1 : 0; + } + + return (block > m_Block) ? 1 : 0; +} + +int mc_RollBackPos::InBlock() +{ + return (m_Block >= 0) ? 1 : 0; +} + +int mc_RollBackPos::InMempool() +{ + return ( (m_Block < 0) && (m_InMempool != 0) ) ? 1 : 0; +} + +int mc_RollBackPos::NotApplied() +{ + return ( (m_Block < 0) && (m_InMempool == 0) ) ? 1 : 0; +} + +int mc_Permissions::SetRollBackPos(int block,int offset,int inmempool) +{ + m_RollBackPos.m_Block=block; + m_RollBackPos.m_Offset=offset; + m_RollBackPos.m_InMempool=inmempool; + + return MC_ERR_NOERROR; +} + +void mc_Permissions::ResetRollBackPos() +{ + m_RollBackPos.Zero(); +} + +/** Rewinds permission sequence to specific position, returns true if mempool should be checked */ + +int mc_Permissions::RewindToRollBackPos(mc_PermissionLedgerRow *row) +{ + if(m_RollBackPos.InBlock() == 0) + { + return MC_ERR_NOERROR; + } + + if(m_Ledger->Open() <= 0) + { + LogString("GetPermission: couldn't open ledger"); + return MC_ERR_DBOPEN_ERROR; + } + + m_Ledger->GetRow(row->m_ThisRow,row); + while( (row->m_PrevRow > 0 ) && m_RollBackPos.IsOut(row->m_BlockReceived,row->m_Offset) ) + { + m_Ledger->GetRow(row->m_PrevRow,row); + } + + if(m_RollBackPos.IsOut(row->m_BlockReceived,row->m_Offset)) + { + row->Zero(); + } + + m_Ledger->Close(); + + return MC_ERR_NOERROR; +} + /** Returns permission value and details for key (entity,address,type) */ uint32_t mc_Permissions::GetPermission(const void* lpEntity,const void* lpAddress,uint32_t type,mc_PermissionLedgerRow *row,int checkmempool) @@ -902,7 +1015,19 @@ uint32_t mc_Permissions::GetPermission(const void* lpEntity,const void* lpAddres m_Ledger->Close(); } - + + if(ptr) + { + if(RewindToRollBackPos(&pldRow)) + { + return 0; + } + if(pldRow.m_Type == MC_PTP_NONE) + { + ptr=NULL; + } + } + if(ptr) { row->m_BlockFrom=pldRow.m_BlockFrom; @@ -923,18 +1048,21 @@ uint32_t mc_Permissions::GetPermission(const void* lpEntity,const void* lpAddres row->m_FoundInDB=found_in_db; */ } - - if(checkmempool) + if(checkmempool != 0) { - mprow=0; - while(mprow>=0) + if( ( m_RollBackPos.NotApplied() != 0) || + ( (m_RollBackPos.InMempool() != 0) && (type != MC_PTP_FILTER) ) ) { - mprow=m_MemPool->Seek((unsigned char*)&pldRow+m_Ledger->m_KeyOffset); - if(mprow>=0) + mprow=0; + while(mprow>=0) { - memcpy((unsigned char*)row+m_Ledger->m_KeyOffset,m_MemPool->GetRow(mprow),m_Ledger->m_TotalSize); -// row->m_FoundInDB=found_in_db; - pldRow.m_PrevRow=row->m_ThisRow; + mprow=m_MemPool->Seek((unsigned char*)&pldRow+m_Ledger->m_KeyOffset); + if(mprow>=0) + { + memcpy((unsigned char*)row+m_Ledger->m_KeyOffset,m_MemPool->GetRow(mprow),m_Ledger->m_TotalSize); + // row->m_FoundInDB=found_in_db; + pldRow.m_PrevRow=row->m_ThisRow; + } } } } @@ -1036,7 +1164,7 @@ int mc_Permissions::IsApprovedInternal(const void* lpUpgrade, int check_current_ /** Returns non-zero value if (entity,address) can connect */ -int mc_Permissions::CanConnect(const void* lpEntity,const void* lpAddress) +int mc_Permissions::CanConnectInternal(const void* lpEntity,const void* lpAddress,int with_implicit) { if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) { @@ -1057,11 +1185,45 @@ int mc_Permissions::CanConnect(const void* lpEntity,const void* lpAddress) result=GetPermission(lpEntity,lpAddress,MC_PTP_CONNECT); + + if(with_implicit) + { + if(result == 0) + { + result |= GetPermission(lpEntity,lpAddress,MC_PTP_ADMIN); + } + + if(result == 0) + { + result |= GetPermission(lpEntity,lpAddress,MC_PTP_ACTIVATE); + } + + if(result == 0) + { + result |= GetPermission(lpEntity,lpAddress,MC_PTP_MINE); + } + } + + if(result) + { + result = MC_PTP_CONNECT; + } + UnLock(); return result; } +int mc_Permissions::CanConnect(const void* lpEntity,const void* lpAddress) +{ + return CanConnectInternal(lpEntity,lpAddress,1); +} + +int mc_Permissions::CanConnectForVerify(const void* lpEntity,const void* lpAddress) +{ + return CanConnectInternal(lpEntity,lpAddress,mc_gState->m_Features->ImplicitConnectPermission()); +} + /** Returns non-zero value if (entity,address) can send */ int mc_Permissions::CanSend(const void* lpEntity,const void* lpAddress) @@ -1219,6 +1381,31 @@ int mc_Permissions::CanWrite(const void* lpEntity,const void* lpAddress) return result; } +/** Returns non-zero value if filter is approved */ + +int mc_Permissions::FilterApproved(const void* lpEntity,const void* lpAddress) +{ + int result; + + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + + Lock(0); + + result = GetPermission(lpEntity,lpAddress,MC_PTP_FILTER); + + if(result) + { + result = MC_PTP_FILTER; + } + + UnLock(); + + return result; +} + /** Returns non-zero value if (entity,address) can write */ int mc_Permissions::CanCreate(const void* lpEntity,const void* lpAddress) @@ -1281,6 +1468,20 @@ int mc_Permissions::CanIssue(const void* lpEntity,const void* lpAddress) return result; } +int mc_Permissions::CanCustom(const void* lpEntity,const void* lpAddress,uint32_t permission) +{ + int result; + + Lock(0); + + result=GetPermission(lpEntity,lpAddress,permission); + + UnLock(); + + return result; +} + + /** Returns 1 if we are still in setup period (NULL entity only) */ int mc_Permissions::IsSetupPeriod() @@ -2178,6 +2379,7 @@ int mc_Permissions::AdminConsensus(const void* lpEntity,uint32_t type) case MC_PTP_ACTIVATE: case MC_PTP_ISSUE: case MC_PTP_CREATE: + case MC_PTP_FILTER: if(IsSetupPeriod()) { return 1; @@ -2204,6 +2406,10 @@ int mc_Permissions::AdminConsensus(const void* lpEntity,uint32_t type) { consensus=mc_gState->m_NetworkParams->GetInt64Param("adminconsensuscreate"); } + if(type == MC_PTP_FILTER) + { + consensus=mc_gState->m_NetworkParams->GetInt64Param("adminconsensustxfilter"); + } if(consensus==0) { @@ -2945,10 +3151,14 @@ void mc_Permissions::FreePermissionList(mc_Buffer *permissions) int mc_Permissions::IsActivateEnough(uint32_t type) { - if(type & ( MC_PTP_ADMIN | MC_PTP_ISSUE | MC_PTP_MINE | MC_PTP_ACTIVATE | MC_PTP_CREATE)) + if(type & ( MC_PTP_ADMIN | MC_PTP_ISSUE | MC_PTP_MINE | MC_PTP_ACTIVATE | MC_PTP_CREATE | MC_PTP_FILTER)) { return 0; } + if(type & GetCustomHighPermissionTypes()) + { + return 0; + } return 1; } @@ -3040,6 +3250,13 @@ int mc_Permissions::SetPermissionInternal(const void* lpEntity,const void* lpAdd types[num_types]=MC_PTP_ACTIVATE;num_types++; types[num_types]=MC_PTP_ADMIN;num_types++; types[num_types]=MC_PTP_UPGRADE;num_types++; + types[num_types]=MC_PTP_FILTER;num_types++; + types[num_types]=MC_PTP_CUSTOM1;num_types++; + types[num_types]=MC_PTP_CUSTOM2;num_types++; + types[num_types]=MC_PTP_CUSTOM3;num_types++; + types[num_types]=MC_PTP_CUSTOM4;num_types++; + types[num_types]=MC_PTP_CUSTOM5;num_types++; + types[num_types]=MC_PTP_CUSTOM6;num_types++; err=MC_ERR_NOERROR; diff --git a/src/permissions/permission.h b/src/permissions/permission.h index 54114203..547e448e 100644 --- a/src/permissions/permission.h +++ b/src/permissions/permission.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAIN_PERMISSION_H @@ -15,17 +15,32 @@ #define MC_PTP_ISSUE 0x00000010 #define MC_PTP_CREATE 0x00000020 #define MC_PTP_MINE 0x00000100 +#define MC_PTP_CUSTOM1 0x00000200 +#define MC_PTP_CUSTOM2 0x00000400 +#define MC_PTP_CUSTOM3 0x00000800 #define MC_PTP_ADMIN 0x00001000 #define MC_PTP_ACTIVATE 0x00002000 #define MC_PTP_UPGRADE 0x00010000 +#define MC_PTP_CUSTOM4 0x00020000 +#define MC_PTP_CUSTOM5 0x00040000 +#define MC_PTP_CUSTOM6 0x00080000 #define MC_PTP_BLOCK_MINER 0x01000000 #define MC_PTP_BLOCK_INDEX 0x02000000 +#define MC_PTP_FILTER 0x04000000 #define MC_PTP_SPECIFIED 0x80000000 #define MC_PTP_ALL 0x00FFFFFF #define MC_PTP_GLOBAL_ALL 0x00003137 +#define MC_PTP_CUSTOM_ALL 0x000E0E00 #define MC_PTP_CACHED_SCRIPT_REQUIRED 0x02000000 // Special temporary value for coin selection +#define MC_PTN_CUSTOM1 "low1" +#define MC_PTN_CUSTOM2 "low2" +#define MC_PTN_CUSTOM3 "low3" +#define MC_PTN_CUSTOM4 "high1" +#define MC_PTN_CUSTOM5 "high2" +#define MC_PTN_CUSTOM6 "high3" + #define MC_PFL_NONE 0x00000000 #define MC_PFL_IS_SCRIPTHASH 0x00000001 @@ -222,6 +237,18 @@ typedef struct mc_PermissionDetails } mc_PermissionDetails; +typedef struct mc_RollBackPos +{ + int m_Block; + int m_Offset; + int m_InMempool; + + void Zero(); + int IsOut(int block,int offset); + int InBlock(); + int InMempool(); + int NotApplied(); +} mc_RollBackPos; typedef struct mc_Permissions @@ -259,7 +286,7 @@ typedef struct mc_Permissions int m_CheckForMempoolFlag; mc_Buffer *m_MempoolPermissions; mc_Buffer *m_MempoolPermissionsToReplay; - + mc_RollBackPos m_RollBackPos; void *m_Semaphore; uint64_t m_LockedBy; @@ -291,12 +318,17 @@ typedef struct mc_Permissions int SetCheckPoint(); int RollBackToCheckPoint(); + int SetRollBackPos(int block,int offset,int inmempool); + void ResetRollBackPos(); + int RewindToRollBackPos(mc_PermissionLedgerRow *row); + uint32_t GetAllPermissions(const void* lpEntity,const void* lpAddress,uint32_t type); uint32_t GetPermissionType(const char *str,uint32_t full_type); -// uint32_t GetPermissionType(const char *str,int entity_type); uint32_t GetPermissionType(const char *str,const void *entity_details); uint32_t GetPossiblePermissionTypes(uint32_t entity_type); uint32_t GetPossiblePermissionTypes(const void *entity_details); + uint32_t GetCustomLowPermissionTypes(); + uint32_t GetCustomHighPermissionTypes(); int GetAdminCount(); int GetMinerCount(); @@ -308,6 +340,7 @@ typedef struct mc_Permissions int IsApproved(const void* lpUpgrade, int check_current_block); int CanConnect(const void* lpEntity,const void* lpAddress); + int CanConnectForVerify(const void* lpEntity,const void* lpAddress); int CanSend(const void* lpEntity,const void* lpAddress); int CanReceive(const void* lpEntity,const void* lpAddress); int CanWrite(const void* lpEntity,const void* lpAddress); @@ -316,6 +349,8 @@ typedef struct mc_Permissions int CanMine(const void* lpEntity,const void* lpAddress); int CanAdmin(const void* lpEntity,const void* lpAddress); int CanActivate(const void* lpEntity,const void* lpAddress); + int CanCustom(const void* lpEntity,const void* lpAddress,uint32_t permission); + int FilterApproved(const void* lpEntity,const void* lpAddress); int CanMineBlock(const void* lpAddress,uint32_t block); @@ -351,6 +386,7 @@ typedef struct mc_Permissions int SetPermissionInternal(const void* lpEntity,const void* lpAddress,uint32_t type,const void* lpAdmin,uint32_t from,uint32_t to,uint32_t timestamp, uint32_t flags,int update_mempool,int offset); + int CanConnectInternal(const void* lpEntity,const void* lpAddress,int with_implicit); int CommitInternal(const void* lpMiner,const void* lpHash); int StoreBlockInfoInternal(const void* lpMiner,const void* lpHash,int update_counts); int RollBackInternal(int block); diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index fe5519c3..3ba859c5 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "primitives/block.h" diff --git a/src/primitives/block.h b/src/primitives/block.h index 99fe3796..427b5243 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_PRIMITIVES_BLOCK_H @@ -22,6 +22,7 @@ enum BLOCKSIGHASH_NONE = 0, BLOCKSIGHASH_HEADER = 1, BLOCKSIGHASH_NO_SIGNATURE_AND_NONCE = 2, + BLOCKSIGHASH_NO_SIGNATURE = 3, BLOCKSIGHASH_INVALID = 0xfe, BLOCKSIGHASH_UNKNOWN = 0xff, }; diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 909cf51d..f9198098 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "primitives/transaction.h" diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 45260d14..e53e3d48 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_PRIMITIVES_TRANSACTION_H diff --git a/src/protocol/handshake.cpp b/src/protocol/handshake.cpp index 3b25285f..201f540e 100644 --- a/src/protocol/handshake.cpp +++ b/src/protocol/handshake.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "core/init.h" diff --git a/src/protocol/multichainblock.cpp b/src/protocol/multichainblock.cpp index 6cdb6a0f..86e97d84 100644 --- a/src/protocol/multichainblock.cpp +++ b/src/protocol/multichainblock.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "core/main.h" @@ -191,33 +191,40 @@ int CreateUpgradeLists(int current_height,vector *vParams, if (it != map_last_upgrade.end()) { take_it=false; - if( ( (param.m_Param->m_Type & MC_PRM_TIME) == 0 ) || - ((*vParams)[it->second].m_Block + MIN_BLOCKS_BETWEEN_UPGRADES <= upgrade.m_AppliedBlock) ) + if(( param.m_Param->m_Type & MC_PRM_DATA_TYPE_MASK) == MC_PRM_BOOLEAN ) { - int64_t old_value=(*vParams)[it->second].m_Value; - if(param.m_Value >= old_value) + take_it=true; + } + else + { + if( ( (param.m_Param->m_Type & MC_PRM_TIME) == 0 ) || + ((*vParams)[it->second].m_Block + MIN_BLOCKS_BETWEEN_UPGRADES <= upgrade.m_AppliedBlock) ) { - if(param_value <= 2*old_value) + int64_t old_value=(*vParams)[it->second].m_Value; + if(param.m_Value >= old_value) { - take_it=true; + if(param_value <= 2*old_value) + { + take_it=true; + } } - } - else - { - if(old_value <= 2*param_value) + else { - take_it=true; - } + if(old_value <= 2*param_value) + { + take_it=true; + } + } + if(!take_it) + { + param.m_Skipped =MC_PSK_DOUBLE_RANGE; + } } - if(!take_it) + else { - param.m_Skipped =MC_PSK_DOUBLE_RANGE; + param.m_Skipped = MC_PSK_FRESH_UPGRADE; } } - else - { - param.m_Skipped = MC_PSK_FRESH_UPGRADE; - } if(take_it) { it->second=(int)vParams->size(); @@ -226,21 +233,28 @@ int CreateUpgradeLists(int current_height,vector *vParams, else { take_it=false; - int64_t old_value=mc_gState->m_NetworkParams->GetInt64Param(param.m_Param->m_Name); - - if(param.m_Value >= old_value) + if(( param.m_Param->m_Type & MC_PRM_DATA_TYPE_MASK) == MC_PRM_BOOLEAN ) { - if(param_value <= 2*old_value) - { - take_it=true; - } + take_it=true; } - else + else { - if(old_value <= 2*param_value) + int64_t old_value=mc_gState->m_NetworkParams->GetInt64Param(param.m_Param->m_Name); + + if(param.m_Value >= old_value) + { + if(param_value <= 2*old_value) + { + take_it=true; + } + } + else { - take_it=true; - } + if(old_value <= 2*param_value) + { + take_it=true; + } + } } if(!take_it) { @@ -450,7 +464,7 @@ void FindSigner(CBlock *block,unsigned char *sig,int *sig_size,uint32_t *hash_ty } } -bool VerifyBlockSignature(CBlock *block,bool force) +bool VerifyBlockSignature(CBlock *block,bool force,bool in_sync) { unsigned char sig[255]; int sig_size;//,key_size; @@ -480,6 +494,28 @@ bool VerifyBlockSignature(CBlock *block,bool force) FindSigner(block, sig, &sig_size, &hash_type); if(block->vSigner[0]) { + if(in_sync) + { + if(Params().DisallowUnsignedBlockNonce()) + { + if(hash_type == BLOCKSIGHASH_NO_SIGNATURE_AND_NONCE) + { + LogPrintf("mchn: Nonce not covered by block signature\n"); + block->nSigHashType=BLOCKSIGHASH_INVALID; + return false; + } + } + else + { + if(hash_type == BLOCKSIGHASH_NO_SIGNATURE) + { + LogPrintf("mchn: Nonce covered by block signature\n"); + block->nSigHashType=BLOCKSIGHASH_INVALID; + return false; + } + } + } + switch(hash_type) { case BLOCKSIGHASH_HEADER: @@ -488,6 +524,7 @@ bool VerifyBlockSignature(CBlock *block,bool force) hash_to_verify=block->GetHash(); break; case BLOCKSIGHASH_NO_SIGNATURE_AND_NONCE: + case BLOCKSIGHASH_NO_SIGNATURE: original_merkle_root=block->hashMerkleRoot; original_nonce=block->nNonce; @@ -495,7 +532,10 @@ bool VerifyBlockSignature(CBlock *block,bool force) block->nMerkleTreeType=MERKLETREE_NO_COINBASE_OP_RETURN; savedMerkleTree=block->vMerkleTree; block->hashMerkleRoot=block->BuildMerkleTree(); - block->nNonce=0; + if(hash_type == BLOCKSIGHASH_NO_SIGNATURE_AND_NONCE) + { + block->nNonce=0; + } hash_to_verify=block->GetHash(); block->hashMerkleRoot=original_merkle_root; diff --git a/src/protocol/multichainscript.cpp b/src/protocol/multichainscript.cpp index 01251313..f2689294 100644 --- a/src/protocol/multichainscript.cpp +++ b/src/protocol/multichainscript.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "multichain/multichain.h" @@ -1287,7 +1287,7 @@ int mc_Script::GetAssetDetails(char* name,int* multiple,unsigned char* script,in return MC_ERR_WRONG_SCRIPT; } - if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+MC_ENT_MAX_SCRIPT_SIZE + 4 + MC_ENT_MAX_NAME_SIZE + 1) + if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+mc_gState->m_Assets->MaxScriptSize() + 4 + MC_ENT_MAX_NAME_SIZE + 1) { return MC_ERR_WRONG_SCRIPT; } @@ -1342,7 +1342,7 @@ int mc_Script::SetAssetDetails(const char*name,int multiple,const unsigned char* int err; unsigned char buf[MC_DCT_SCRIPT_IDENTIFIER_LEN+1+4]; - if((script_size<0) || (script_size>MC_ENT_MAX_SCRIPT_SIZE)) + if((script_size<0) || (script_size>mc_gState->m_Assets->MaxScriptSize())) { return MC_ERR_INVALID_PARAMETER_VALUE; } @@ -1402,7 +1402,7 @@ int mc_Script::GetGeneralDetails(unsigned char* script,int *script_size) return MC_ERR_WRONG_SCRIPT; } - if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+MC_ENT_MAX_SCRIPT_SIZE) + if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+mc_gState->m_Assets->MaxScriptSize()) { return MC_ERR_WRONG_SCRIPT; } @@ -1437,7 +1437,7 @@ int mc_Script::SetGeneralDetails(const unsigned char* script,int script_size) int err; unsigned char buf[MC_DCT_SCRIPT_IDENTIFIER_LEN+1+4]; - if((script_size<0) || (script_size>MC_ENT_MAX_SCRIPT_SIZE)) + if((script_size<0) || (script_size>mc_gState->m_Assets->MaxScriptSize())) { return MC_ERR_INVALID_PARAMETER_VALUE; } @@ -1540,7 +1540,7 @@ int mc_Script::GetNewEntityType(uint32_t *type,int *update,unsigned char* script return MC_ERR_WRONG_SCRIPT; } - if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+1+MC_ENT_MAX_SCRIPT_SIZE) + if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+1+mc_gState->m_Assets->MaxScriptSize()) { return MC_ERR_WRONG_SCRIPT; } @@ -1590,7 +1590,7 @@ int mc_Script::SetNewEntityType(const uint32_t type,const int update,const unsig int err; unsigned char buf[MC_DCT_SCRIPT_IDENTIFIER_LEN+1+1]; - if((script_size<0) || (script_size>MC_ENT_MAX_SCRIPT_SIZE)) + if((script_size<0) || (script_size>mc_gState->m_Assets->MaxScriptSize())) { return MC_ERR_INVALID_PARAMETER_VALUE; } diff --git a/src/protocol/multichainscript.h b/src/protocol/multichainscript.h index 9bd48084..a00c55dc 100644 --- a/src/protocol/multichainscript.h +++ b/src/protocol/multichainscript.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAINSCRIPT_H diff --git a/src/protocol/multichaintx.cpp b/src/protocol/multichaintx.cpp index 705169e2..ea484415 100644 --- a/src/protocol/multichaintx.cpp +++ b/src/protocol/multichaintx.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "core/main.h" @@ -10,6 +10,9 @@ #include "multichain/multichain.h" #include "structs/base58.h" #include "custom/custom.h" +#include "filters/multichainfilter.h" + +extern mc_MultiChainFilterEngine* pMultiChainFilterEngine; using namespace std; @@ -24,6 +27,9 @@ bool ExtractDestinations10008(const CScript& scriptPubKey, txnouttype& typeRet, #define MC_MTX_OUTPUT_DETAIL_FLAG_NOT_OP_RETURN 0x00000010 #define MC_MTX_OUTPUT_DETAIL_FLAG_FOLLOWON_DETAILS 0x00000020 #define MC_MTX_OUTPUT_DETAIL_FLAG_NO_DESTINATION 0x00000040 +#define MC_MTX_OUTPUT_DETAIL_FLAG_PERMISSION_FILTER 0x00000080 +#define MC_MTX_OUTPUT_DETAIL_FLAG_PERMISSION_NOT_FILTER 0x00000100 +#define MC_MTX_OUTPUT_DETAIL_FLAG_LICENSE_TRANSFER 0x00000200 typedef struct CMultiChainTxDetails { @@ -33,6 +39,10 @@ typedef struct CMultiChainTxDetails bool fSeedNodeInvolved; // Connect permission of seed node changed bool fFullReplayCheckRequired; // Tx should be rechecked when mempool is replayed bool fAdminMinerGrant; // Admin/miner grant in this transaction + bool fAssetIssuance; // New/followon issuance in this tx + bool fIsStandardCoinbase; // Tx is standard coinbase - filters should not be applied + bool fLicenseTokenIssuance; // New license token + bool fLicenseTokenTransfer; // License token transfer vector vInputScriptTypes; // Input script types vector vInputDestinations; // Addresses used in input scripts @@ -42,6 +52,7 @@ typedef struct CMultiChainTxDetails set vAllowedAdmins; // Admin permissions before this tx - for grants set vAllowedActivators; // Activate permissions before this tx - for grants + set vRelevantEntities; // Set of entities involved in this transaction vector vOutputScriptFlags; // Output script flags, filled when script is processed for the first time vector vOutputPermissionRequired; // Number of required receive permissions @@ -57,6 +68,7 @@ typedef struct CMultiChainTxDetails int64_t total_offchain_size; // Total size of offchain items int64_t total_value_in; // Total amount in inputs int64_t total_value_out; // Total amount in outputs + int emergency_disapproval_output; // Output carrying emergency filter disapproval - bypassed by filters CMultiChainTxDetails() { @@ -70,6 +82,8 @@ typedef struct CMultiChainTxDetails void Zero(); bool IsRelevantInput(int vin,int vout); + void SetRelevantEntity(void *entity); + bool IsRelevantEntity(uint160 hash); } CMultiChainTxDetails; @@ -95,7 +109,11 @@ void CMultiChainTxDetails::Zero() fSeedNodeInvolved=false; fFullReplayCheckRequired=false; fAdminMinerGrant=false; - + fAssetIssuance=false; + fIsStandardCoinbase=false; + fLicenseTokenIssuance=false; + fLicenseTokenTransfer=false; + details_script_size=0; details_script_type=-1; new_entity_type=MC_ENT_TYPE_NONE; @@ -103,6 +121,7 @@ void CMultiChainTxDetails::Zero() total_offchain_size=0; total_value_in=0; total_value_out=0; + emergency_disapproval_output=-1; } bool CMultiChainTxDetails::IsRelevantInput(int vin, int vout) @@ -114,6 +133,25 @@ bool CMultiChainTxDetails::IsRelevantInput(int vin, int vout) return false; } +bool CMultiChainTxDetails::IsRelevantEntity(uint160 hash) +{ + if(vRelevantEntities.find(hash) != vRelevantEntities.end()) + { + return true; + } + return false; +} + +void CMultiChainTxDetails::SetRelevantEntity(void *entity) +{ + uint160 hash=0; + memcpy(&hash,entity,MC_AST_SHORT_TXID_SIZE); + if(!IsRelevantEntity(hash)) + { + vRelevantEntities.insert(hash); + } +} + uint160 mc_GenesisAdmin(const CTransaction& tx) { uint32_t type,from,to,timestamp; @@ -207,7 +245,7 @@ bool mc_ExtractInputAssetQuantities(mc_Buffer *assets, const CScript& script1, u return true; } -bool mc_CompareAssetQuantities(string& reason) +bool mc_CompareAssetQuantities(CMultiChainTxDetails *details,string& reason) { unsigned char *ptrIn; unsigned char *ptrOut; @@ -250,6 +288,8 @@ bool mc_CompareAssetQuantities(string& reason) return false; } + details->SetRelevantEntity((unsigned char*)entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET); + if(quantity>0) { if(row>=0) @@ -876,6 +916,8 @@ bool MultiChainTransaction_CheckEntityItem(const CTransaction& tx, return false; } + details->SetRelevantEntity(short_txid); + if(entity.GetEntityType() == MC_ENT_TYPE_ASSET) // Asset update { if(!MultiChainTransaction_CheckAssetUpdateDetails(&entity,vout,details,reason)) @@ -894,10 +936,17 @@ bool MultiChainTransaction_CheckEntityItem(const CTransaction& tx, } else // (Pseudo)stream item { - if(!MultiChainTransaction_CheckStreamItem(&entity,vout,details,reason)) + if(entity.GetEntityType() <= MC_ENT_TYPE_STREAM_MAX) { - return false; - } + if(!MultiChainTransaction_CheckStreamItem(&entity,vout,details,reason)) + { + return false; + } + } + else + { + reason="Metadata script rejected - too many elements for this entity type"; + } } } @@ -1019,7 +1068,10 @@ bool MultiChainTransaction_ProcessPermissions(const CTransaction& tx, { reason="Script rejected - entity not found"; return false; - } + } + + details->emergency_disapproval_output=-2; // Only global filter disapprovals can bypass filters + details->SetRelevantEntity(short_txid); } else // Not entity element { @@ -1033,9 +1085,29 @@ bool MultiChainTransaction_ProcessPermissions(const CTransaction& tx, if(fFirstPass) { - if( type & ( MC_PTP_CREATE | MC_PTP_ISSUE | MC_PTP_ACTIVATE ) ) + if( type & ( MC_PTP_CREATE | MC_PTP_ISSUE | MC_PTP_ACTIVATE | MC_PTP_FILTER ) ) { details->vOutputScriptFlags[vout] |= MC_MTX_OUTPUT_DETAIL_FLAG_PERMISSION_CREATE; + } + if(type & mc_gState->m_Permissions->GetCustomHighPermissionTypes()) + { + details->vOutputScriptFlags[vout] |= MC_MTX_OUTPUT_DETAIL_FLAG_PERMISSION_CREATE; + } + if( type & MC_PTP_FILTER ) + { + details->vOutputScriptFlags[vout] |= MC_MTX_OUTPUT_DETAIL_FLAG_PERMISSION_FILTER; + if( (type == MC_PTP_FILTER) && (details->emergency_disapproval_output == -1) ) + { + details->emergency_disapproval_output=vout; + } + else + { + details->emergency_disapproval_output=-2; // non-standard emergency disapproval + } + } + if( type != MC_PTP_FILTER) + { + details->vOutputScriptFlags[vout] |= MC_MTX_OUTPUT_DETAIL_FLAG_PERMISSION_NOT_FILTER; } if( type & ( MC_PTP_MINE | MC_PTP_ADMIN ) ) { @@ -1111,6 +1183,22 @@ bool MultiChainTransaction_ProcessPermissions(const CTransaction& tx, bool fAdminFound=false; bool fAdminFoundWithoutCachedScript=false; bool fActivateIsEnough=mc_gState->m_Permissions->IsActivateEnough(type); + + + if(details->emergency_disapproval_output == vout) + { + if(mc_gState->m_Permissions->FilterApproved(NULL,ptr) == 0) // Already disapproved + { + details->emergency_disapproval_output=-2; + } + if(mc_gState->m_Features->FixedIn20005()) // Not standard disapproval + { + if( (to != 0) || (from != 0) ) + { + details->emergency_disapproval_output=-2; + } + } + } for (unsigned int i = 0; i < tx.vin.size(); i++) { @@ -1184,6 +1272,121 @@ bool MultiChainTransaction_ProcessPermissions(const CTransaction& tx, return true; } +bool Is_MultiChainLicenseTokenTransfer(const CTransaction& tx) +{ + mc_EntityDetails entity; + if(tx.IsCoinBase()) + { + return false; + } + if(tx.vout.size() != 1) + { + return false; + } + if(tx.vin.size() != 1) + { + return false; + } + if(mc_gState->m_Features->LicenseTokens() == 0) + { + return false; + } + + MultiChainTransaction_SetTmpOutputScript(tx.vout[0].scriptPubKey); + if(mc_gState->m_TmpScript->GetNumElements() == 0) + { + return false; + } + + mc_gState->m_TmpAssetsOut->Clear(); + mc_gState->m_TmpScript->SetElement(0); + if(mc_gState->m_TmpScript->GetAssetQuantities(mc_gState->m_TmpAssetsOut,MC_SCR_ASSET_SCRIPT_TYPE_TRANSFER) != MC_ERR_NOERROR) + { + return false; + } + + if(mc_gState->m_Assets->FindEntityByFullRef(&entity,mc_gState->m_TmpAssetsOut->GetRow(0))) + { + if(entity.GetEntityType() == MC_ENT_TYPE_LICENSE_TOKEN) + { + return true; + } + } + + return false; +} + +bool MultiChainTransaction_CheckLicenseTokenTransfer(const CTransaction& tx, + int unchecked_row, + CMultiChainTxDetails *details, + string& reason) +{ + bool token_transfer=false; + mc_EntityDetails entity; + + if(mc_gState->m_Features->LicenseTokens() == 0) + { + return true; + } + + for(int i=unchecked_row;im_TmpAssetsOut->GetCount();i++) + { + if(mc_gState->m_Assets->FindEntityByFullRef(&entity,mc_gState->m_TmpAssetsOut->GetRow(i))) + { + if(entity.GetEntityType() == MC_ENT_TYPE_LICENSE_TOKEN) + { + token_transfer=true; + } + } + } + + if(!token_transfer) + { + return true; + } + + if(tx.vout.size() != 1) + { + reason="License token transfer tx should have one output"; + return false; + } + + if(tx.vin.size() != 1) + { + reason="License token transfer tx should have one input"; + return false; + } + + if(mc_gState->m_TmpScript->GetNumElements() != 3) + { + reason="License token transfer script rejected - wrong number of elements"; + return false; + } + + if(details->vInputHashTypes[0] != SIGHASH_ALL) + { + reason="License token transfer script rejected - wrong signature type"; + return false; + } + + if(mc_gState->m_TmpAssetsOut->GetCount() != 1) + { + reason="License token transfer script rejected - wrong script"; + return false; + } + + if(mc_GetABQuantity(mc_gState->m_TmpAssetsOut->GetRow(0)) != 1) + { + reason="License token transfer script rejected - wrong number of license token units"; + return false; + } + + details->vOutputScriptFlags[0] |= MC_MTX_OUTPUT_DETAIL_FLAG_LICENSE_TRANSFER; + details->fLicenseTokenTransfer=true; + return true; +} + + bool MultiChainTransaction_CheckAssetTransfers(const CTransaction& tx, int offset, int vout, @@ -1205,13 +1408,22 @@ bool MultiChainTransaction_CheckAssetTransfers(const CTransaction& tx, } } + int unchecked_row=mc_gState->m_TmpAssetsOut->GetCount(); if(!mc_ExtractOutputAssetQuantities(mc_gState->m_TmpAssetsOut,reason,false))// Filling output asset quantity list { return false; + } + + if(!MultiChainTransaction_CheckLicenseTokenTransfer(tx,unchecked_row,details,reason)) + { + return false; } + + // Check for dust and receive permissions // Not required for pure grants - if(details->vOutputScriptFlags[vout] & MC_MTX_OUTPUT_DETAIL_FLAG_NOT_PURE_PERMISSION) + if( ( (details->vOutputScriptFlags[vout] & MC_MTX_OUTPUT_DETAIL_FLAG_NOT_PURE_PERMISSION) != 0 ) && + ( (details->vOutputScriptFlags[vout] & MC_MTX_OUTPUT_DETAIL_FLAG_LICENSE_TRANSFER) == 0 ) ) { if((offset < 0) && Params().RequireStandard()) // If not in block - part of IsStandard check { @@ -1304,6 +1516,7 @@ bool MultiChainTransaction_CheckOutputs(const CTransaction& tx, } permission_type=MC_PTP_CONNECT | MC_PTP_SEND | MC_PTP_RECEIVE | MC_PTP_WRITE; + permission_type |= mc_gState->m_Permissions->GetCustomLowPermissionTypes(); if(!MultiChainTransaction_ProcessPermissions(tx,offset,vout,permission_type,true,details,reason)) { return false; @@ -1313,11 +1526,12 @@ bool MultiChainTransaction_CheckOutputs(const CTransaction& tx, for (unsigned int vout = 0; vout < tx.vout.size(); vout++) // create, issue, activate grants (requiring pre-tx permissions) { - if(details->vOutputScriptFlags[vout] & MC_MTX_OUTPUT_DETAIL_FLAG_PERMISSION_CREATE) + if(details->vOutputScriptFlags[vout] & (MC_MTX_OUTPUT_DETAIL_FLAG_PERMISSION_CREATE | MC_MTX_OUTPUT_DETAIL_FLAG_PERMISSION_FILTER) ) { MultiChainTransaction_SetTmpOutputScript(tx.vout[vout].scriptPubKey); - permission_type=MC_PTP_CREATE | MC_PTP_ISSUE | MC_PTP_ACTIVATE; + permission_type=MC_PTP_CREATE | MC_PTP_ISSUE | MC_PTP_ACTIVATE | MC_PTP_FILTER; + permission_type |= mc_gState->m_Permissions->GetCustomHighPermissionTypes(); if(!MultiChainTransaction_ProcessPermissions(tx,offset,vout,permission_type,false,details,reason)) { return false; @@ -1339,8 +1553,6 @@ bool MultiChainTransaction_CheckOutputs(const CTransaction& tx, } } - mc_gState->m_TmpAssetsOut->Clear(); - for (unsigned int vout = 0; vout < tx.vout.size(); vout++) { // Entity items (stream items, upgrade approvals, updates) @@ -1352,7 +1564,22 @@ bool MultiChainTransaction_CheckOutputs(const CTransaction& tx, { return false; } - } + } // Assets quantities and permission checks + } + + return true; +} + + +bool MultiChainTransaction_CheckTransfers(const CTransaction& tx, // Tx to check + int offset, // Tx offset in block, -1 if in memppol + CMultiChainTxDetails *details, // Tx details object + string& reason) // Error message +{ + mc_gState->m_TmpAssetsOut->Clear(); + + for (unsigned int vout = 0; vout < tx.vout.size(); vout++) + { // Assets quantities and permission checks if(details->vOutputScriptFlags[vout] & MC_MTX_OUTPUT_DETAIL_FLAG_NOT_OP_RETURN) { @@ -1365,11 +1592,11 @@ bool MultiChainTransaction_CheckOutputs(const CTransaction& tx, } } - if(!mc_CompareAssetQuantities(reason)) // Comparing input/output asset quantities + if(!mc_CompareAssetQuantities(details,reason)) // Comparing input/output asset quantities { return false; } - + return true; } @@ -1396,7 +1623,125 @@ bool MultiChainTransaction_CheckMandatoryFee(CMultiChainTxDetails *details, return true; } +bool MultiChainTransaction_CheckLicenseTokenDetails(CMultiChainTxDetails *details, // Tx details object + void *token_address, // Address the token is issued to + string& reason) // Error message +{ + uint32_t offset,next_offset,param_value_start; + unsigned int timestamp; + size_t param_value_size; + size_t value_sizes[256]; + int value_starts[256]; + unsigned char code; + + memset(value_sizes,0,256*sizeof(size_t)); + memset(value_starts,0,256*sizeof(int)); + + offset=0; + + while((int)offsetdetails_script_size) + { + next_offset=mc_GetParamFromDetailsScript(details->details_script,details->details_script_size,offset,¶m_value_start,¶m_value_size); + if(param_value_start > 0) + { + if(details->details_script[offset] == 0) + { + code=details->details_script[offset+1]; + if(value_starts[code]) + { + reason="License token issue script rejected - multiple values for the same code in details script"; + return false; + } + value_sizes[code]=param_value_size; + value_starts[code]=param_value_start; + } + } + offset=next_offset; + } + + if( (value_starts[MC_ENT_SPRM_NAME] == 0) || (value_sizes[MC_ENT_SPRM_NAME] == 0) ) + { + reason="License token issue script rejected - no name"; + return false; + } + if( value_sizes[MC_ENT_SPRM_LICENSE_REQUEST_HASH] == 0 ) + { + reason="License token issue script rejected - invalid request hash"; + return false; + } + if( value_sizes[MC_ENT_SPRM_LICENSE_REQUEST_ADDRESS] != sizeof(uint160) ) + { + reason="License token issue script rejected - invalid request address"; + return false; + } + if(memcmp(token_address,details->details_script+value_starts[MC_ENT_SPRM_LICENSE_REQUEST_ADDRESS],value_sizes[MC_ENT_SPRM_LICENSE_REQUEST_ADDRESS])) + { + reason="License token issue script rejected - request address mismatch"; + return false; + } + if( value_sizes[MC_ENT_SPRM_LICENSE_CONFIRMATION_TIME] < 4 ) + { + reason="License token issue script rejected - invalid confirmation time"; + return false; + } + if( value_sizes[MC_ENT_SPRM_LICENSE_CONFIRMATION_REF] == 0 ) + { + reason="License token issue script rejected - invalid confirmation reference"; + return false; + } + if( value_sizes[MC_ENT_SPRM_LICENSE_PUBKEY] == 0 ) + { + reason="License token issue script rejected - invalid pubkey"; + return false; + } + if( (value_sizes[MC_ENT_SPRM_LICENSE_MIN_VERSION] < 4 ) || + (value_sizes[MC_ENT_SPRM_LICENSE_MIN_VERSION] > 8 )) + { + reason="License token issue script rejected - invalid version"; + return false; + } + if( mc_gState->GetNumericVersion() < mc_GetLE(details->details_script+value_starts[MC_ENT_SPRM_LICENSE_MIN_VERSION],value_sizes[MC_ENT_SPRM_LICENSE_MIN_VERSION]) ) + { + reason="License token issue script rejected - Not supported in this version of MultiChain"; + return false; + } + if( (value_sizes[MC_ENT_SPRM_LICENSE_MIN_PROTOCOL] < 4 ) || + (value_sizes[MC_ENT_SPRM_LICENSE_MIN_PROTOCOL] > 8 )) + { + reason="License token issue script rejected - invalid protocol"; + return false; + } + if( mc_gState->m_NetworkParams->ProtocolVersion() < mc_GetLE(details->details_script+value_starts[MC_ENT_SPRM_LICENSE_MIN_PROTOCOL],value_sizes[MC_ENT_SPRM_LICENSE_MIN_PROTOCOL]) ) + { + reason="License token issue script rejected - Not supported in this protocol version"; + return false; + } + if( value_sizes[MC_ENT_SPRM_LICENSE_SIGNATURE] == 0 ) + { + reason="License token issue script rejected - invalid signature"; + return false; + } + if( (value_sizes[MC_ENT_SPRM_TIMESTAMP] != 4 )) + { + reason="License token issue script rejected - invalid timestamp"; + return false; + } + + timestamp=(unsigned int)mc_GetLE(details->details_script+value_starts[MC_ENT_SPRM_TIMESTAMP],value_sizes[MC_ENT_SPRM_TIMESTAMP]); + if(timestamp < chainActive.Tip()->nTime-30*86400) + { + reason="License token issue script rejected - timestamp is too far in the past"; + return false; + } + if(timestamp > chainActive.Tip()->nTime+30*86400) + { + reason="License token issue script rejected - timestamp is too far in the future"; + return false; + } + + return true; +} bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, // Tx to check int offset, // Tx offset in block, -1 if in memppol @@ -1410,7 +1755,7 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, mc_EntityDetails entity; mc_EntityDetails this_entity; char asset_name[MC_ENT_MAX_NAME_SIZE+1]; - int multiple; + int multiple,out_count,issue_vout; int err; int64_t quantity,total; uint256 txid; @@ -1423,6 +1768,7 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, size_t value_size; unsigned char short_txid[MC_AST_SHORT_TXID_SIZE]; CTxDestination addressRet; + unsigned char token_address[20]; if(tx.IsCoinBase()) { @@ -1441,6 +1787,9 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, multiple=1; new_issue=false; follow_on=false; + out_count=0; + issue_vout=-1; + if(details->details_script_type == 0) // New asset with details script { @@ -1482,7 +1831,6 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, reason="Metadata script rejected - wrong element, should be entityref"; return false; } - } if(details->vOutputScriptFlags[vout] & MC_MTX_OUTPUT_DETAIL_FLAG_NOT_OP_RETURN) @@ -1498,6 +1846,11 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, err=mc_gState->m_TmpScript->GetAssetGenesis(&quantity); if(err == 0) // Asset genesis issuance { + out_count++; + if(e == 0) + { + issue_vout=vout; + } issue_in_output=true; new_issue=true; if(quantity+total<0) @@ -1527,14 +1880,17 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, if(lpKeyID != NULL) { address=CBitcoinAddress(*lpKeyID); + memcpy(token_address,lpKeyID,sizeof(uint160)); } else { address=CBitcoinAddress(*lpScriptID); + memcpy(token_address,lpScriptID,sizeof(uint160)); } if(update_mempool) { - if(fDebug)LogPrint("mchn","Found asset issue script in tx %s for %s - (%ld)\n", + if(fDebug)LogPrint("mchn","Found %s issue script in tx %s for %s - (%ld)\n", + (details->new_entity_type == MC_ENT_TYPE_LICENSE_TOKEN) ? "license token" : "asset", tx.GetHash().GetHex().c_str(), address.ToString().c_str(),quantity); } @@ -1627,6 +1983,7 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, reason="Details script rejected - entity not found"; return false; } + details->SetRelevantEntity(short_txid); } else { @@ -1637,7 +1994,7 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, return false; } } - + if(entity.AllowedFollowOns() == 0) { reason="Asset follow-on script rejected - follow-ons not allowed for this asset"; @@ -1651,6 +2008,10 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, return false; } } + if(mc_gState->m_Features->FixedIn20005()) // Issumore was missed before 20005 + { + details->SetRelevantEntity((unsigned char*)entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET); + } if(ptrOut) { total=mc_GetABQuantity(ptrOut); @@ -1662,17 +2023,71 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, } } + if(details->new_entity_type == MC_ENT_TYPE_LICENSE_TOKEN) + { + if(!new_issue) + { + reason="License token issue script rejected - issuance output not found"; + return false; + } + } + if(!new_issue && !follow_on) { return true; } + details->fAssetIssuance=true; + if(details->new_entity_output >= 0) { if(details->new_entity_type != MC_ENT_TYPE_ASSET) { - reason="Asset issue script rejected - not allowed in this transaction, conflicts with other entities"; - return false; + if(details->new_entity_type == MC_ENT_TYPE_LICENSE_TOKEN) + { + if(mc_gState->m_Features->LicenseTokens()) + { + if(out_count != 1) + { + reason="License token issue script rejected - should have exactly one issuance output"; + return false; + } + if(multiple != 1) + { + reason="License token issue script rejected - should have multiple=1"; + return false; + } + if(total != 1) + { + reason="License token issue script rejected - should have single unit"; + return false; + } + if(issue_vout < 0) + { + reason="License token issue script rejected - issuance element should be the first element in script"; + return false; + } + + MultiChainTransaction_SetTmpOutputScript(tx.vout[issue_vout].scriptPubKey); + if(mc_gState->m_TmpScript->GetNumElements() != 3) + { + reason="License token issue script rejected - wrong number of elements"; + return false; + } + + if(!MultiChainTransaction_CheckLicenseTokenDetails(details,token_address,reason)) + { + return false; + } + details->vOutputScriptFlags[issue_vout] |= MC_MTX_OUTPUT_DETAIL_FLAG_LICENSE_TRANSFER; + details->fLicenseTokenIssuance=true; + } + } + if(!details->fLicenseTokenIssuance) + { + reason="Asset issue script rejected - not allowed in this transaction, conflicts with other entities"; + return false; + } } } @@ -1692,6 +2107,7 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, { can_issue=true; } + can_issue |= details->fLicenseTokenIssuance; } else { @@ -1724,57 +2140,61 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, mc_gState->m_TmpScript->Clear(); mc_gState->m_TmpScript->AddElement(); - unsigned char issuer_buf[24]; - memset(issuer_buf,0,sizeof(issuer_buf)); - flags=MC_PFL_NONE; - uint32_t timestamp=0; - set stored_issuers; - - // First per-entity record in permission database - // We'll need it for scanning asset-related rows - if(new_issue) + + if(!details->fLicenseTokenIssuance) { - err=MC_ERR_NOERROR; + unsigned char issuer_buf[24]; + memset(issuer_buf,0,sizeof(issuer_buf)); + flags=MC_PFL_NONE; + uint32_t timestamp=0; + set stored_issuers; - txid=tx.GetHash(); - err=mc_gState->m_Permissions->SetPermission(&txid,issuer_buf,MC_PTP_CONNECT, - (unsigned char*)issuers[0].begin(),0,(uint32_t)(-1),timestamp,flags | MC_PFL_ENTITY_GENESIS ,update_mempool,offset); - } + // First per-entity record in permission database + // We'll need it for scanning asset-related rows + if(new_issue) + { + err=MC_ERR_NOERROR; - uint32_t all_permissions=MC_PTP_ADMIN | MC_PTP_ISSUE; - if(mc_gState->m_Features->PerAssetPermissions()) - { - all_permissions |= MC_PTP_ACTIVATE | MC_PTP_SEND | MC_PTP_RECEIVE; - } - - for (unsigned int i = 0; i < issuers.size(); i++) // Setting per-asset permissions and creating issuers script - { - if(err == MC_ERR_NOERROR) + txid=tx.GetHash(); + err=mc_gState->m_Permissions->SetPermission(&txid,issuer_buf,MC_PTP_CONNECT, + (unsigned char*)issuers[0].begin(),0,(uint32_t)(-1),timestamp,flags | MC_PFL_ENTITY_GENESIS ,update_mempool,offset); + } + + uint32_t all_permissions=MC_PTP_ADMIN | MC_PTP_ISSUE; + if(mc_gState->m_Features->PerAssetPermissions()) + { + all_permissions |= MC_PTP_ACTIVATE | MC_PTP_SEND | MC_PTP_RECEIVE; + } + + for (unsigned int i = 0; i < issuers.size(); i++) // Setting per-asset permissions and creating issuers script { - if(stored_issuers.count(issuers[i]) == 0) + if(err == MC_ERR_NOERROR) { - memcpy(issuer_buf,issuers[i].begin(),sizeof(uint160)); - mc_PutLE(issuer_buf+sizeof(uint160),&issuer_flags[i],4); - if((int)i < mc_gState->m_Assets->MaxStoredIssuers()) // Adding list of issuers to the asset script + if(stored_issuers.count(issuers[i]) == 0) { - mc_gState->m_TmpScript->SetSpecialParamValue(MC_ENT_SPRM_ISSUER,issuer_buf,sizeof(issuer_buf)); - } - if(new_issue) // Setting first permission record - to scan from - { - err=mc_gState->m_Permissions->SetPermission(&txid,issuer_buf,all_permissions, - (unsigned char*)issuers[0].begin(),0,(uint32_t)(-1),timestamp,flags | MC_PFL_ENTITY_GENESIS ,update_mempool,offset); + memcpy(issuer_buf,issuers[i].begin(),sizeof(uint160)); + mc_PutLE(issuer_buf+sizeof(uint160),&issuer_flags[i],4); + if((int)i < mc_gState->m_Assets->MaxStoredIssuers()) // Adding list of issuers to the asset script + { + mc_gState->m_TmpScript->SetSpecialParamValue(MC_ENT_SPRM_ISSUER,issuer_buf,sizeof(issuer_buf)); + } + if(new_issue) // Setting first permission record - to scan from + { + err=mc_gState->m_Permissions->SetPermission(&txid,issuer_buf,all_permissions, + (unsigned char*)issuers[0].begin(),0,(uint32_t)(-1),timestamp,flags | MC_PFL_ENTITY_GENESIS ,update_mempool,offset); + } + stored_issuers.insert(issuers[i]); } - stored_issuers.insert(issuers[i]); } - } - } + } - memset(issuer_buf,0,sizeof(issuer_buf)); - mc_gState->m_TmpScript->SetSpecialParamValue(MC_ENT_SPRM_ISSUER,issuer_buf,1); - if(err) - { - reason="Cannot update permission database for issued asset"; - return false; + memset(issuer_buf,0,sizeof(issuer_buf)); + mc_gState->m_TmpScript->SetSpecialParamValue(MC_ENT_SPRM_ISSUER,issuer_buf,1); + if(err) + { + reason="Cannot update permission database for issued asset"; + return false; + } } const unsigned char *special_script; @@ -1783,7 +2203,8 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, txid=tx.GetHash(); if(new_issue) // Updating entity database { - err=mc_gState->m_Assets->InsertAsset(&txid,offset,total,asset_name,multiple,details->details_script,details->details_script_size,special_script,special_script_size,update_mempool); + err=mc_gState->m_Assets->InsertAsset(&txid,offset,details->fLicenseTokenIssuance ? MC_ENT_TYPE_LICENSE_TOKEN : MC_ENT_TYPE_ASSET, + total,asset_name,multiple,details->details_script,details->details_script_size,special_script,special_script_size,update_mempool); } else { @@ -1821,7 +2242,8 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, { if(new_issue) { - if(fDebug)LogPrint("mchn","New asset. TxID: %s, AssetRef: %d-%d-%d, Name: %s\n", + if(fDebug)LogPrint("mchn","New %s. TxID: %s, AssetRef: %d-%d-%d, Name: %s\n", + details->fLicenseTokenIssuance ? "license token" : "asset", tx.GetHash().GetHex().c_str(), mc_gState->m_Assets->m_Block+1,offset,(int)(*((unsigned char*)&txid+0))+256*(int)(*((unsigned char*)&txid+1)), this_entity.GetName()); @@ -1862,6 +2284,11 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, return true; } + if(details->new_entity_type == MC_ENT_TYPE_LICENSE_TOKEN) // Processed in another place + { + return true; + } + vector openers; vector opener_flags; unsigned char opener_buf[24]; @@ -1872,6 +2299,7 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, uint32_t timestamp=0; set stored_openers; int update_mempool; + bool check_admin=true; uint256 txid; mc_EntityDetails entity; @@ -1890,8 +2318,35 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, if(details->IsRelevantInput(i,details->new_entity_output)) { if(mc_gState->m_Permissions->CanCreate(NULL,(unsigned char*)&(details->vInputDestinations[i]))) - { - if( (details->new_entity_type != MC_ENT_TYPE_UPGRADE) || (mc_gState->m_Permissions->CanAdmin(NULL,(unsigned char*)&(details->vInputDestinations[i])) != 0) ) + { + if(details->new_entity_type <= MC_ENT_TYPE_STREAM_MAX) + { + check_admin=false; + } + else + { + if(details->new_entity_type == MC_ENT_TYPE_FILTER) + { + if(mc_gState->m_Features->StreamFilters()) + { + uint32_t value_offset; + size_t value_size; + value_offset=mc_FindSpecialParamInDetailsScript(details->details_script,details->details_script_size,MC_ENT_SPRM_FILTER_TYPE,&value_size); + if(value_offset<(uint32_t)details->details_script_size) + { + if((uint32_t)mc_GetLE(details->details_script+value_offset,value_size) == MC_FLT_TYPE_STREAM) + { + check_admin=false; + } + } + + } + } + } + +// if( (details->new_entity_type <= MC_ENT_TYPE_STREAM_MAX) || // Admin persmission is required for upgrades and filters + if( !check_admin || + (mc_gState->m_Permissions->CanAdmin(NULL,(unsigned char*)&(details->vInputDestinations[i])) != 0) ) { openers.push_back(details->vInputDestinations[i]); flags=MC_PFL_NONE; @@ -1916,14 +2371,14 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, mc_gState->m_TmpScript->Clear(); mc_gState->m_TmpScript->AddElement(); txid=tx.GetHash(); // Setting first record in the per-entity permissions list - - if(details->new_entity_type != MC_ENT_TYPE_UPGRADE) + + if(details->new_entity_type <= MC_ENT_TYPE_STREAM_MAX) { memset(opener_buf,0,sizeof(opener_buf)); err=mc_gState->m_Permissions->SetPermission(&txid,opener_buf,MC_PTP_CONNECT, (unsigned char*)openers[0].begin(),0,(uint32_t)(-1),timestamp, MC_PFL_ENTITY_GENESIS ,update_mempool,offset); } - + for (unsigned int i = 0; i < openers.size(); i++) { if(err == MC_ERR_NOERROR) @@ -1936,7 +2391,7 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, { mc_gState->m_TmpScript->SetSpecialParamValue(MC_ENT_SPRM_ISSUER,opener_buf,sizeof(opener_buf)); } - if(details->new_entity_type != MC_ENT_TYPE_UPGRADE) + if(details->new_entity_type <= MC_ENT_TYPE_STREAM_MAX) { // Granting default per-entity permissions to openers err=mc_gState->m_Permissions->SetPermission(&txid,opener_buf,MC_PTP_ADMIN | MC_PTP_ACTIVATE | MC_PTP_WRITE, @@ -1984,6 +2439,10 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, { entity_type_str="upgrade"; } + if(details->new_entity_type == MC_ENT_TYPE_FILTER) + { + entity_type_str="filter"; + } if(offset>=0) { LogPrintf("New %s. TxID: %s, StreamRef: %d-%d-%d, Name: %s\n", @@ -1996,6 +2455,10 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, LogPrintf("New %s. TxID: %s, unconfirmed, Name: %s\n", entity_type_str.c_str(),tx.GetHash().GetHex().c_str(),entity.GetName()); } + if(details->new_entity_type == MC_ENT_TYPE_FILTER) + { + pMultiChainFilterEngine->Add((unsigned char*)&txid+MC_AST_SHORT_TXID_OFFSET,(offset < 0) ? 0 : 1); + } } else { @@ -2007,6 +2470,194 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, return true; } +bool MultiChainTransaction_VerifyNotFilteredRestrictions(const CTransaction& tx, // Tx to check + int offset, // Tx offset in block, -1 if in memppol + bool accept, // Accept to mempools if successful + CMultiChainTxDetails *details, // Tx details object + string& reason) // Error message +{ + if(details->emergency_disapproval_output < 0) + { + return true; + } + + if(tx.IsCoinBase()) + { + details->emergency_disapproval_output=-2; + return true; + } + + if(tx.vin.size() > 1) + { + details->emergency_disapproval_output=-2; + return true; + } + + if(details->vInputDestinations.size() != 1) + { + details->emergency_disapproval_output=-2; + return true; + } + + if(tx.vout.size() > 2) + { + details->emergency_disapproval_output=-2; + return true; + } + + for (unsigned int vout = 0; vout < tx.vout.size(); vout++) + { + if(mc_gState->m_Features->FixedIn20005()) + { + if( (details->vOutputScriptFlags[vout] & MC_MTX_OUTPUT_DETAIL_FLAG_NOT_OP_RETURN) == 0) + { + details->emergency_disapproval_output=-2; + return true; + } + } + else + { + if(details->vOutputScriptFlags[vout] & MC_MTX_OUTPUT_DETAIL_FLAG_NOT_OP_RETURN) + { + details->emergency_disapproval_output=-2; + return true; + } + } + + MultiChainTransaction_SetTmpOutputScript(tx.vout[vout].scriptPubKey); + + if((int)vout == details->emergency_disapproval_output) + { + if(mc_gState->m_TmpScript->GetNumElements() > 1) + { + details->emergency_disapproval_output=-2; + return true; + } + } + else + { + mc_gState->m_TmpAssetsTmp->Clear(); + for (int e = 0; e < mc_gState->m_TmpScript->GetNumElements(); e++) + { + mc_gState->m_TmpScript->SetElement(e); + if(mc_gState->m_TmpScript->GetAssetQuantities(mc_gState->m_TmpAssetsTmp,MC_SCR_ASSET_SCRIPT_TYPE_TRANSFER) != MC_ERR_NOERROR) + { + details->emergency_disapproval_output=-2; + return true; + } + } + + CKeyID *lpKeyID=boost::get (&(details->vOutputSingleDestination[vout])); + CScriptID *lpScriptID=boost::get (&(details->vOutputSingleDestination[vout])); + if(((lpKeyID == NULL) && (lpScriptID == NULL)) || (details->vOutputDestinations[vout].size() != 1)) + { + details->emergency_disapproval_output=-2; + return true; + } + if(lpKeyID) + { + if(*(uint160*)lpKeyID != details->vInputDestinations[0]) + { + details->emergency_disapproval_output=-2; + return true; + } + } + if(lpScriptID) + { + if(*(uint160*)lpScriptID != details->vInputDestinations[0]) + { + details->emergency_disapproval_output=-2; + return true; + } + } + } + } + + if(details->emergency_disapproval_output >= 0) + { + if(fDebug)LogPrint("filter","Standard filter disapproval - tx will bypass all filter\n"); + } + return true; +} + +bool MultiChainTransaction_VerifyStandardCoinbase(const CTransaction& tx, // Tx to check + CMultiChainTxDetails *details, // Tx details object + string& reason) // Error message +{ + details->fIsStandardCoinbase=false; + + if(!tx.IsCoinBase()) + { + return true; + } + + if(mc_gState->m_Features->FixedIn20005() == 0) // Coinbase is checked like all other txs before 20005 + { + return true; + } + + bool value_output=false; + bool signature_output=false; + for (unsigned int vout = 0; vout < tx.vout.size(); vout++) + { + MultiChainTransaction_SetTmpOutputScript(tx.vout[vout].scriptPubKey); + + if(mc_gState->m_TmpScript->IsOpReturnScript()) + { + if(signature_output) + { + LogPrintf("Non-standard coinbase: Multiple signatures\n"); + return true; + } + if(mc_gState->m_TmpScript->GetNumElements() != 1) + { + if(mc_gState->m_Permissions->m_Block >= 0) + { + LogPrintf("Non-standard coinbase: Non-standard signature\n"); + return true; + } + } + signature_output=true; + } + else + { + if(tx.vout[vout].nValue == 0) + { + if(value_output) + { + LogPrintf("Non-standard coinbase: Multiple value outputs\n"); + return true; // Only single "value" output is allowed + } + if(mc_gState->m_Permissions->m_Block > 0) + { + LogPrintf("Non-standard coinbase: Value output for chain without native currency\n"); + return true; // No "value" outputs for the blocks 2+ + } + } + value_output=true; + } + + } + + if(!value_output) + { + if(mc_gState->m_Permissions->m_Block <= 0) + { + LogPrintf("Non-standard coinbase: Missing value output\n"); + } + } + + if(signature_output == (mc_gState->m_NetworkParams->IsProtocolMultichain() == 0)) + { + LogPrintf("Non-standard coinbase: Sigature output doesn't match protocol\n"); + return true; // Signature output should appear only if protocol=multichain + } + + details->fIsStandardCoinbase=true; + + return true; +} + bool AcceptMultiChainTransaction (const CTransaction& tx, // Tx to check const CCoinsViewCache &inputs, // Tx inputs from UTXO database int offset, // Tx offset in block, -1 if in memppol @@ -2037,25 +2688,31 @@ bool AcceptMultiChainTransaction (const CTransaction& tx, } mc_gState->m_Permissions->SetCheckPoint(); // if there is an error after this point or it is just check, permission mempool should be restored - + mc_gState->m_Assets->SetCheckPoint(); + if(!MultiChainTransaction_CheckOutputs(tx,inputs,offset,&details,reason)) // Outputs { fReject=true; goto exitlbl; } + + if(!MultiChainTransaction_ProcessAssetIssuance(tx,offset,accept,&details,reason)) // Asset genesis/followon + { + fReject=true; + goto exitlbl; + } if(!MultiChainTransaction_CheckMandatoryFee(&details,&mandatory_fee,reason)) { fReject=true; goto exitlbl; - } - // Asset genesis/followon - if(!MultiChainTransaction_ProcessAssetIssuance(tx,offset,accept,&details,reason)) + } + + if(!MultiChainTransaction_CheckTransfers(tx,offset,&details,reason)) // Transfers and receive permissions { fReject=true; goto exitlbl; } - // Creating of (pseudo)streams/upgrades if(!MultiChainTransaction_ProcessEntityCreation(tx,offset,accept,&details,reason)) { @@ -2063,6 +2720,49 @@ bool AcceptMultiChainTransaction (const CTransaction& tx, goto exitlbl; } + if(!MultiChainTransaction_VerifyNotFilteredRestrictions(tx,offset,accept,&details,reason)) + { + fReject=true; + goto exitlbl; + } + + if(!MultiChainTransaction_VerifyStandardCoinbase(tx,&details,reason)) + { + fReject=true; + goto exitlbl; + } + + if( (details.emergency_disapproval_output < 0) && !details.fIsStandardCoinbase && !details.fLicenseTokenTransfer) + { + if(mc_gState->m_Features->Filters()) + { + if(pMultiChainFilterEngine) + { + mc_MultiChainFilter* lpFilter; + int applied=0; + + if(pMultiChainFilterEngine->RunTxFilters(tx,details.vRelevantEntities,reason,&lpFilter,&applied) != MC_ERR_NOERROR) + { + reason="Error while running filters"; + fReject=true; + goto exitlbl; + } + else + { + if(reason.size()) + { + if(fDebug)LogPrint("mchn","mchn: Rejecting filter: %s\n",lpFilter->m_FilterCaption.c_str()); + fReject=true; + goto exitlbl; + } + if(applied) + { + details.fFullReplayCheckRequired=true; + } + } + } + } + } // Custom filters fReject=!custom_accept_transacton(tx,inputs,offset,accept,reason,replay); @@ -2086,6 +2786,7 @@ bool AcceptMultiChainTransaction (const CTransaction& tx, if(!accept || fReject) // Rolling back permission database if we were just checking or error occurred { mc_gState->m_Permissions->RollBackToCheckPoint(); + mc_gState->m_Assets->RollBackToCheckPoint(); } if(mandatory_fee_out) diff --git a/src/protocol/netprotocol.cpp b/src/protocol/netprotocol.cpp index 0474c134..c1737bb1 100644 --- a/src/protocol/netprotocol.cpp +++ b/src/protocol/netprotocol.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "protocol/netprotocol.h" diff --git a/src/protocol/netprotocol.h b/src/protocol/netprotocol.h index a123a994..cefe2f78 100644 --- a/src/protocol/netprotocol.h +++ b/src/protocol/netprotocol.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef __cplusplus diff --git a/src/protocol/relay.cpp b/src/protocol/relay.cpp index 5540e225..e56b38c1 100644 --- a/src/protocol/relay.cpp +++ b/src/protocol/relay.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "protocol/relay.h" diff --git a/src/protocol/relay.h b/src/protocol/relay.h index 82cfee02..de4f6d77 100644 --- a/src/protocol/relay.h +++ b/src/protocol/relay.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. diff --git a/src/rpc/rpcasio.h b/src/rpc/rpcasio.h new file mode 100644 index 00000000..05055f30 --- /dev/null +++ b/src/rpc/rpcasio.h @@ -0,0 +1,89 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2014-2016 The Bitcoin Core developers +// Original code was distributed under the MIT software license. +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef BITCOIN_RPCASIO_H +#define BITCOIN_RPCASIO_H + +#include +#include +#include +#include +#include + +#include +#include + +/** + * IOStream device that speaks SSL but can also speak non-SSL + */ +template +class SSLIOStreamDevice : public boost::iostreams::device { +public: + SSLIOStreamDevice(boost::asio::ssl::stream &streamIn, bool fUseSSLIn) : stream(streamIn) + { + fUseSSL = fUseSSLIn; + fNeedHandshake = fUseSSLIn; + } + + void handshake(boost::asio::ssl::stream_base::handshake_type role) + { + if (!fNeedHandshake) return; + fNeedHandshake = false; + stream.handshake(role); + } + std::streamsize read(char* s, std::streamsize n) + { + handshake(boost::asio::ssl::stream_base::server); // HTTPS servers read first + if (fUseSSL) return stream.read_some(boost::asio::buffer(s, n)); + return stream.next_layer().read_some(boost::asio::buffer(s, n)); + } + std::streamsize write(const char* s, std::streamsize n) + { + handshake(boost::asio::ssl::stream_base::client); // HTTPS clients write first + if (fUseSSL) return boost::asio::write(stream, boost::asio::buffer(s, n)); + return boost::asio::write(stream.next_layer(), boost::asio::buffer(s, n)); + } + bool connect(const std::string& server, const std::string& port) + { + using namespace boost::asio::ip; + tcp::resolver resolver(stream.get_io_service()); + tcp::resolver::iterator endpoint_iterator; +#if BOOST_VERSION >= 104300 + try { +#endif + // The default query (flags address_configured) tries IPv6 if + // non-localhost IPv6 configured, and IPv4 if non-localhost IPv4 + // configured. + tcp::resolver::query query(server.c_str(), port.c_str()); + endpoint_iterator = resolver.resolve(query); +#if BOOST_VERSION >= 104300 + } catch(boost::system::system_error &e) + { + // If we at first don't succeed, try blanket lookup (IPv4+IPv6 independent of configured interfaces) + tcp::resolver::query query(server.c_str(), port.c_str(), resolver_query_base::flags()); + endpoint_iterator = resolver.resolve(query); + } +#endif + boost::system::error_code error = boost::asio::error::host_not_found; + tcp::resolver::iterator end; + while (error && endpoint_iterator != end) + { + stream.lowest_layer().close(); + stream.lowest_layer().connect(*endpoint_iterator++, error); + } + if (error) + return false; + return true; + } + +private: + bool fNeedHandshake; + bool fUseSSL; + boost::asio::ssl::stream& stream; +}; + + +#endif // BITCOIN_RPCASIO_H diff --git a/src/rpc/rpcassets.cpp b/src/rpc/rpcassets.cpp index 94b86ebc..3a2c193f 100644 --- a/src/rpc/rpcassets.cpp +++ b/src/rpc/rpcassets.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. @@ -289,6 +289,7 @@ Value issuefromcmd(const Array& params, bool fHelp) addresses.push_back(address.Get()); + EnsureWalletIsUnlocked(); if(params[0].get_str() != "*") { @@ -337,7 +338,6 @@ Value issuefromcmd(const Array& params, bool fHelp) } } - EnsureWalletIsUnlocked(); { LOCK (pwalletMain->cs_wallet_send); @@ -502,6 +502,7 @@ Value issuemorefromcmd(const Array& params, bool fHelp) addresses.push_back(address.Get()); + EnsureWalletIsUnlocked(); if(params[0].get_str() != "*") @@ -600,7 +601,6 @@ Value issuemorefromcmd(const Array& params, bool fHelp) } - EnsureWalletIsUnlocked(); { LOCK (pwalletMain->cs_wallet_send); @@ -1478,6 +1478,33 @@ Value gettotalbalances(const Array& params, bool fHelp) return getassetbalances(new_params,fHelp); } +Value getassetinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + mc_ThrowHelpMessage("getassetinfo"); +// throw runtime_error("Help message not found\n"); + + if(params[0].type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid asset identifier, expected string"); + } + + uint32_t output_level; + mc_EntityDetails entity; + ParseEntityIdentifier(params[0].get_str(),&entity, MC_ENT_TYPE_ASSET); + + output_level=0x07; + + if (params.size() > 1) + { + if(paramtobool(params[1])) + { + output_level|=0x20; + } + } + + return AssetEntry(entity.GetTxID(),-1,output_level); +} Value listassets(const Array& params, bool fHelp) { @@ -1715,9 +1742,9 @@ Object ListAssetTransactions(const CWalletTx& wtx, mc_EntityDetails *entity, boo aFormatMetaDataPerOutput.resize(wtx.vout.size()); - for (int i = 0; i < (int)wtx.vout.size(); ++i) + for (int j = 0; j < (int)wtx.vout.size(); ++j) { - const CTxOut& txout = wtx.vout[i]; + const CTxOut& txout = wtx.vout[j]; if(!txout.scriptPubKey.IsUnspendable()) { string strFailReason; @@ -1765,7 +1792,7 @@ Object ListAssetTransactions(const CWalletTx& wtx, mc_EntityDetails *entity, boo } else { - const CScript& script2 = wtx.vout[i].scriptPubKey; + const CScript& script2 = wtx.vout[j].scriptPubKey; CScript::const_iterator pc2 = script2.begin(); lpScript->Clear(); @@ -1781,9 +1808,9 @@ Object ListAssetTransactions(const CWalletTx& wtx, mc_EntityDetails *entity, boo { // elem = lpScript->GetData(lpScript->GetNumElements()-1,&elem_size); retrieve_status = GetFormattedData(lpScript,&elem,&out_size,chunk_hashes,chunk_count,total_chunk_size); - Value metadata=OpReturnFormatEntry(elem,out_size,wtx.GetHash(),i,format,NULL,retrieve_status); + Value metadata=OpReturnFormatEntry(elem,out_size,wtx.GetHash(),j,format,NULL,retrieve_status); aFormatMetaData.push_back(metadata); - aFormatMetaDataPerOutput[i].push_back(metadata); + aFormatMetaDataPerOutput[j].push_back(metadata); } } else @@ -1795,7 +1822,7 @@ Object ListAssetTransactions(const CWalletTx& wtx, mc_EntityDetails *entity, boo if(out_size) { // aMetaData.push_back(OpReturnEntry(elem,elem_size,wtx.GetHash(),i)); - aFormatMetaData.push_back(OpReturnFormatEntry(elem,out_size,wtx.GetHash(),i,format,NULL,retrieve_status)); + aFormatMetaData.push_back(OpReturnFormatEntry(elem,out_size,wtx.GetHash(),j,format,NULL,retrieve_status)); } } @@ -1892,6 +1919,194 @@ Object ListAssetTransactions(const CWalletTx& wtx, mc_EntityDetails *entity, boo return entry; } +Value getfilterassetbalances(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + mc_ThrowHelpMessage("getfilterassetbalances"); + + mc_EntityDetails asset_entity; + mc_EntityDetails* lpAsset=NULL; + double multiple=1.; + if(COIN > 0) + { + multiple=(double)COIN; + } + + int64_t quantity; + bool raw_value=false; + + if (params.size() > 1) + { + raw_value=paramtobool(params[1]); + } + + if(params[0].type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid asset identifier, expected string"); + } + + if(params[0].get_str().size()) + { + ParseEntityIdentifier(params[0],&asset_entity,MC_ENT_TYPE_ASSET); + lpAsset=&asset_entity; + multiple=(double)(lpAsset->GetAssetMultiple()); + } + + map mAddresses; + + mc_Buffer *asset_amounts=mc_gState->m_TmpBuffers->m_RpcABBuffer1; + asset_amounts->Clear(); + + mc_Script *lpScript=mc_gState->m_TmpBuffers->m_RpcScript3; + lpScript->Clear(); + + for(int j=0;j<(int)pMultiChainFilterEngine->m_Tx.vin.size();j++) + { + int n=pMultiChainFilterEngine->m_Tx.vin[j].prevout.n; + CCoins coins; + { + LOCK(mempool.cs); + CCoinsViewMemPool view(pcoinsTip, mempool); + if (!view.GetCoins(pMultiChainFilterEngine->m_Tx.vin[j].prevout.hash, coins)) + { + return Value::null; + } + if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) + return Value::null; + + CTxDestination address; + int required=0; + + ExtractDestination(coins.vout[n].scriptPubKey, address); + + quantity=-1; + + if(lpAsset) + { + asset_amounts->Clear(); + if(CreateAssetBalanceList(coins.vout[n],asset_amounts,lpScript,&required)) + { + for(int i=0;iGetCount();i++) + { + if(memcmp(lpAsset->GetFullRef(),asset_amounts->GetRow(i),MC_AST_ASSET_FULLREF_SIZE) == 0) + { + quantity=mc_GetABQuantity(asset_amounts->GetRow(i)); + } + else + { + int gin=j; + if(mc_gState->m_Features->FixedIn20006() == 0) + { + gin=i; + } + if(memcmp(lpAsset->GetTxID(),&(pMultiChainFilterEngine->m_Tx.vin[gin].prevout.hash),sizeof(uint256)) == 0) + { + if( mc_GetABRefType(asset_amounts->GetRow(i)) == MC_AST_ASSET_REF_TYPE_GENESIS ) + { + quantity=mc_GetABQuantity(asset_amounts->GetRow(i)); + } + } + } + + } + } + } + else + { + quantity=coins.vout[n].nValue; + } + if(quantity >= 0) + { + map::iterator itold = mAddresses.find(address); + if (itold == mAddresses.end()) + { + mAddresses.insert(make_pair(address, -quantity)); + } + else + { + itold->second-=quantity; + } + } + } + } + for (int j = 0; j < (int)pMultiChainFilterEngine->m_Tx.vout.size(); ++j) + { + const CTxOut& txout = pMultiChainFilterEngine->m_Tx.vout[j]; + if(!txout.scriptPubKey.IsUnspendable()) + { + CTxDestination address; + int required=0; + + ExtractDestination(txout.scriptPubKey, address); + + quantity=-1; + if(lpAsset) + { + asset_amounts->Clear(); + if(CreateAssetBalanceList(txout,asset_amounts,lpScript,&required)) + { + for(int i=0;iGetCount();i++) + { + if(memcmp(lpAsset->GetFullRef(),asset_amounts->GetRow(i),MC_AST_ASSET_FULLREF_SIZE) == 0) + { + quantity=mc_GetABQuantity(asset_amounts->GetRow(i)); + } + else + { + if(memcmp(lpAsset->GetTxID(),&(pMultiChainFilterEngine->m_TxID),sizeof(uint256)) == 0) + { + if( mc_GetABRefType(asset_amounts->GetRow(i)) == MC_AST_ASSET_REF_TYPE_GENESIS ) + { + quantity=mc_GetABQuantity(asset_amounts->GetRow(i)); + } + } + } + } + } + } + else + { + quantity=txout.nValue; + } + if(quantity >= 0) + { + map::iterator itold = mAddresses.find(address); + if (itold == mAddresses.end()) + { + mAddresses.insert(make_pair(address, +quantity)); + } + else + { + itold->second+=quantity; + } + } + } + } + + int64_t other_amount=0; + + Object oBalance; + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& item, mAddresses) + { + const CTxDestination dest=item.first; + const CKeyID *lpKeyID=boost::get (&dest); + const CScriptID *lpScript=boost::get (&dest); + if( (lpKeyID == NULL) && (lpScript == NULL) ) + { + other_amount=item.second; + } + else + { + oBalance.push_back(Pair(CBitcoinAddress(item.first).ToString(), raw_value ? item.second : item.second/multiple)); + } + } + if(other_amount != 0) + { + oBalance.push_back(Pair("", raw_value ? other_amount : other_amount/multiple)); + } + + return oBalance; +} Value getassettransaction(const Array& params, bool fHelp) { @@ -2012,7 +2227,7 @@ Value listassettransactions(const Array& params, bool fHelp) mc_Script *lpScript=mc_gState->m_TmpBuffers->m_RpcScript3; lpScript->Clear(); - pwalletTxsMain->GetList(&entStat.m_Entity,1,1,entity_rows); + CheckWalletError(pwalletTxsMain->GetList(&entStat.m_Entity,1,1,entity_rows)); shift=1; if(entity_rows->GetCount()) { @@ -2040,7 +2255,7 @@ Value listassettransactions(const Array& params, bool fHelp) } } - pwalletTxsMain->GetList(&entStat.m_Entity,start+1,count,entity_rows); + CheckWalletError(pwalletTxsMain->GetList(&entStat.m_Entity,start+1,count,entity_rows)); for(int i=0;iGetCount()+shift;i++) diff --git a/src/rpc/rpcblockchain.cpp b/src/rpc/rpcblockchain.cpp index 60789819..e9819000 100644 --- a/src/rpc/rpcblockchain.cpp +++ b/src/rpc/rpcblockchain.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "chain/checkpoints.h" @@ -387,6 +387,49 @@ Value listblocks(const Array& params, bool fHelp) return result; } +Value getlastblockinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + mc_ThrowHelpMessage("getlastblockinfo"); +// throw runtime_error("Help message not found\n"); + + CBlockIndex* pblockindex = chainActive.Tip(); + + if(params.size() == 1) + { + if(params[0].type() != int_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Skip should be integer"); + } + + int skip=params[0].get_int(); + if (skip < 0 || skip > chainActive.Height()) + throw JSONRPCError(RPC_BLOCK_NOT_FOUND, "Skip out of range"); + + pblockindex=chainActive[chainActive.Height() - skip]; + } + + Object result; + result.push_back(Pair("hash", pblockindex->GetBlockHash().GetHex())); + result.push_back(Pair("height", pblockindex->nHeight)); + result.push_back(Pair("time", pblockindex->GetBlockTime())); + result.push_back(Pair("txcount", (int)pblockindex->nTx)); + + CKeyID keyID; + Value miner; + if(mc_gState->m_NetworkParams->IsProtocolMultichain()) + { + if(mc_gState->m_Permissions->GetBlockMiner(pblockindex->nHeight,(unsigned char*)&keyID) == MC_ERR_NOERROR) + { + miner=CBitcoinAddress(keyID).ToString(); + } + } + result.push_back(Pair("miner", miner)); + + + return result; +} + /* MCHN END */ @@ -395,21 +438,37 @@ Value getblock(const Array& params, bool fHelp) if (fHelp || params.size() < 1 || params.size() > 2) // MCHN throw runtime_error("Help message not found\n"); - std::string strHash = params[0].get_str(); - if(strHash.size() < 64) + int nHeight=-1; + bool is_hash=true; + std::string strHash; + + if(params[0].type() == int_type) { - int nHeight; - if(!StringToInt(params[0].get_str(),&nHeight)) + nHeight=params[0].get_int(); + is_hash=false; + } + else + { + strHash = params[0].get_str(); + if( strHash.size() < 64 ) { - throw JSONRPCError(RPC_BLOCK_NOT_FOUND, "Block height should be integer"); - } - -// int nHeight = atoi(params[0].get_str().c_str()); + if(!StringToInt(params[0].get_str(),&nHeight)) + { + throw JSONRPCError(RPC_BLOCK_NOT_FOUND, "Block height should be integer"); + } + is_hash=false; + } + } + + if(!is_hash) + { if (nHeight < 0 || nHeight > chainActive.Height()) throw JSONRPCError(RPC_BLOCK_NOT_FOUND, "Block height out of range"); - strHash=chainActive[nHeight]->GetBlockHash().GetHex(); + strHash=chainActive[nHeight]->GetBlockHash().GetHex(); } + +// int nHeight = atoi(params[0].get_str().c_str()); uint256 hash(strHash); /* bool fVerbose = true; @@ -438,10 +497,18 @@ Value getblock(const Array& params, bool fHelp) if (mapBlockIndex.count(hash) == 0) throw JSONRPCError(RPC_BLOCK_NOT_FOUND, "Block not found"); - + CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; + if(pMultiChainFilterEngine->m_TxID != 0) + { + if (!chainActive.Contains(pblockindex)) + { + throw JSONRPCError(RPC_BLOCK_NOT_FOUND, "Block not found in active chain"); + } + } + if(!ReadBlockFromDisk(block, pblockindex)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); @@ -478,6 +545,32 @@ Value gettxoutsetinfo(const Array& params, bool fHelp) return ret; } +Value getfiltertxinput(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + mc_ThrowHelpMessage("getfiltertxinput"); +// throw JSONRPCError(RPC_INVALID_PARAMS, "Wrong number of parameters"); + + int64_t vin = params[0].get_int64(); + + if(pMultiChainFilterEngine->m_Vout >= 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "This callback cannot be used in stream filters"); + } + + if( (vin < 0) || (vin >= (unsigned int)pMultiChainFilterEngine->m_Tx.vin.size()) ) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "vin out of range"); + } + + + Array getxoutparams; + getxoutparams.push_back(pMultiChainFilterEngine->m_Tx.vin[vin].prevout.hash.ToString()); + getxoutparams.push_back((int64_t)pMultiChainFilterEngine->m_Tx.vin[vin].prevout.n); + + return gettxout(getxoutparams,false); +} + Value gettxout(const Array& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 3) // MCHN @@ -498,7 +591,10 @@ Value gettxout(const Array& params, bool fHelp) CCoinsViewMemPool view(pcoinsTip, mempool); if (!view.GetCoins(hash, coins)) return Value::null; - mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool + if(pMultiChainFilterEngine->m_TxID == 0) // In filter we already checked this input exists, but mempool is dirty + { + mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool + } } else { if (!pcoinsTip->GetCoins(hash, coins)) return Value::null; diff --git a/src/rpc/rpccache.cpp b/src/rpc/rpccache.cpp index 87d1dd9f..fd1b0dc1 100644 --- a/src/rpc/rpccache.cpp +++ b/src/rpc/rpccache.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "rpc/rpcutils.h" diff --git a/src/rpc/rpcchunks.cpp b/src/rpc/rpcchunks.cpp index d7c0a6eb..74ef4466 100644 --- a/src/rpc/rpcchunks.cpp +++ b/src/rpc/rpcchunks.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "core/init.h" diff --git a/src/rpc/rpcclient.cpp b/src/rpc/rpcclient.cpp index b46e6148..2efbfc95 100644 --- a/src/rpc/rpcclient.cpp +++ b/src/rpc/rpcclient.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "rpc/rpcclient.h" @@ -23,6 +23,203 @@ class CRPCConvertParam int paramIdx; //! 0-based idx of param to convert }; +static const std::string vAPINames[] = +{ +"addmultisigaddress", +"addnode", +"addresses-all", +"appendbinarycache", +"appendrawchange", +"appendrawdata", +"appendrawexchange", +"appendrawmetadata", +"appendrawtransaction", +"approvefrom", +"backupwallet", +"clearmempool", +"combineunspent", +"completerawexchange", +"create", +"createbinarycache", +"createfrom", +"createkeypairs", +"createmultisig", +"createrawexchange", +"createrawsendfrom", +"createrawtransaction", +"data-all", +"data-with", +"debug", +"decoderawexchange", +"decoderawtransaction", +"decodescript", +"deletebinarycache", +"disablerawtransaction", +"dumpprivkey", +"dumpwallet", +"encryptwallet", +"estimatefee", +"estimatepriority", +"filters", +"getaccount", +"getaccountaddress", +"getaddednodeinfo", +"getaddressbalances", +"getaddresses", +"getaddressesbyaccount", +"getaddresstransaction", +"getassetbalances", +"getassetinfo", +"getassettransaction", +"getbalance", +"getbestblockhash", +"getblock", +"getblockchaininfo", +"getblockchainparams", +"getblockcount", +"getblockhash", +"getblocktemplate", +"getchaintips", +"getchunkqueueinfo", +"getchunkqueuetotals", +"getconnectioncount", +"getdifficulty", +"getfilterassetbalances", +"getfiltercode", +"getfilterstreamitem", +"getfiltertransaction", +"getfiltertxid", +"getfiltertxinput", +"getgenerate", +"gethashespersec", +"getinfo", +"getlastblockinfo", +"getmempoolinfo", +"getmininginfo", +"getmultibalances", +"getnettotals", +"getnetworkhashps", +"getnetworkinfo", +"getnewaddress", +"getpeerinfo", +"getrawchangeaddress", +"getrawmempool", +"getrawtransaction", +"getreceivedbyaccount", +"getreceivedbyaddress", +"getruntimeparams", +"getstreaminfo", +"getstreamitem", +"getstreamkeysummary", +"getstreampublishersummary", +"gettotalbalances", +"gettransaction", +"gettxout", +"gettxoutdata", +"gettxoutsetinfo", +"getunconfirmedbalance", +"getwalletinfo", +"getwallettransaction", +"grant", +"grantfrom", +"grantwithdata", +"grantwithdatafrom", +"grantwithmetadata", +"grantwithmetadatafrom", +"help", +"importaddress", +"importprivkey", +"importwallet", +"invalidateblock", +"issue", +"issuefrom", +"issuemore", +"issuemorefrom", +"keypoolrefill", +"listaccounts", +"listaddresses", +"listaddressgroupings", +"listaddresstransactions", +"listassets", +"listassettransactions", +"listblocks", +"listlockunspent", +"listpermissions", +"listreceivedbyaccount", +"listreceivedbyaddress", +"listsinceblock", +"liststreamblockitems", +"liststreamfilters", +"liststreamitems", +"liststreamkeyitems", +"liststreamkeys", +"liststreampublisheritems", +"liststreampublishers", +"liststreamqueryitems", +"liststreams", +"liststreamtxitems", +"listtransactions", +"listtxfilters", +"listunspent", +"listupgrades", +"listwallettransactions", +"lockunspent", +"move", +"pause", +"ping", +"preparelockunspent", +"preparelockunspentfrom", +"prioritisetransaction", +"publish", +"publishfrom", +"publishmulti", +"publishmultifrom", +"reconsiderblock", +"resendwallettransactions", +"resume", +"revoke", +"revokefrom", +"runstreamfilter", +"runtxfilter", +"send", +"sendasset", +"sendassetfrom", +"sendassettoaddress", +"sendfrom", +"sendfromaccount", +"sendfromaddress", +"sendmany", +"sendrawtransaction", +"sendtoaddress", +"sendwithdata", +"sendwithdatafrom", +"sendwithmetadata", +"sendwithmetadatafrom", +"setaccount", +"setfilterparam", +"setgenerate", +"setlastblock", +"setmocktime", +"setruntimeparam", +"settxfee", +"signmessage", +"signrawtransaction", +"stop", +"storechunk", +"submitblock", +"subscribe", +"teststreamfilter", +"testtxfilter", +"unsubscribe", +"validateaddress", +"verifychain", +"verifymessage", +"verifypermission", +"walletlock", +"walletpassphrase", +"walletpassphrasechange" +}; + static const CRPCConvertParam vRPCConvertParams[] = { { "stop", 0 }, @@ -64,6 +261,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "issuemore", 2 }, { "issuemore", 3 }, { "issuemore", 4 }, + { "getassetinfo", 1 }, + { "getstreaminfo", 1 }, + { "getfiltertxinput", 0 }, { "listassets", 0 }, { "listassets", 1 }, { "listassets", 2 }, @@ -76,10 +276,20 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listupgrades", 1 }, { "listupgrades", 2 }, { "listupgrades", 3 }, + { "listtxfilters", 0 }, + { "listtxfilters", 1 }, + { "liststreamfilters", 0 }, + { "liststreamfilters", 1 }, + { "testtxfilter", 0 }, + { "teststreamfilter", 0 }, + { "teststreamfilter", 3 }, + { "runstreamfilter", 2 }, { "publishfrom", 2 }, { "publishfrom", 3 }, { "publish", 1 }, { "publish", 2 }, + { "publishmultifrom", 2 }, + { "publishmulti", 1 }, { "getassetbalances", 1 }, { "getassetbalances", 2 }, { "getassetbalances", 3 }, @@ -90,6 +300,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "sendassettoaddress", 3 }, { "sendasset", 2 }, { "sendasset", 3 }, + { "getlastblockinfo", 0 }, { "getblockchainparams", 0 }, { "getblockchainparams", 1 }, { "preparelockunspent", 0 }, @@ -187,6 +398,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "gettxoutdata", 1 }, { "gettxoutdata", 2 }, { "gettxoutdata", 3 }, + { "txouttobinarycache", 2 }, + { "txouttobinarycache", 3 }, + { "txouttobinarycache", 4 }, { "liststreamkeys", 1 }, { "liststreamkeys", 2 }, { "liststreamkeys", 3 }, @@ -296,6 +510,30 @@ class CRPCConvertTable } }; +class CRPCNameTable +{ +private: + std::set members; + +public: + CRPCNameTable(); + + bool found(const std::string& method) { + return (members.count(method) > 0); + } +}; + +CRPCNameTable::CRPCNameTable() +{ + const unsigned int n_elem = + (sizeof(vAPINames) / sizeof(vAPINames[0])); + + for (unsigned int i = 0; i < n_elem; i++) { + members.insert(vAPINames[i]); + } +} + + CRPCConvertTable::CRPCConvertTable() { const unsigned int n_elem = @@ -308,6 +546,7 @@ CRPCConvertTable::CRPCConvertTable() } static CRPCConvertTable rpcCvtTable; +static CRPCNameTable rpcNamTable; /* MCHN START */ @@ -346,6 +585,8 @@ static const CRPCConvertParamMayBeString vRPCConvertParamsMayBeString[] = { "listassets", 0 }, { "liststreams", 0 }, { "listupgrades", 0 }, + { "listtxfilters", 0 }, + { "liststreamfilters", 0 }, { "listpermissions", 1 }, { "publishfrom", 2 }, { "publishfrom", 3 }, @@ -354,6 +595,8 @@ static const CRPCConvertParamMayBeString vRPCConvertParamsMayBeString[] = { "setgenerate", 0 }, { "liststreamblockitems", 1 }, { "listblocks", 0 }, + { "createfrom", 4 }, + { "create", 3 }, }; class CRPCConvertTableMayBeString @@ -382,6 +625,12 @@ CRPCConvertTableMayBeString::CRPCConvertTableMayBeString() static CRPCConvertTableMayBeString rpcCvtTableMayBeString; + +bool HaveAPIWithThisName(const std::string &strMethod) +{ + return rpcNamTable.found(strMethod); +} + /* MCHN END */ /** Convert strings to command-specific RPC representation */ diff --git a/src/rpc/rpcclient.h b/src/rpc/rpcclient.h index 88e16e53..9213854c 100644 --- a/src/rpc/rpcclient.h +++ b/src/rpc/rpcclient.h @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_RPCCLIENT_H @@ -12,5 +12,6 @@ #include "json/json_spirit_writer_template.h" json_spirit::Array RPCConvertValues(const std::string& strMethod, const std::vector& strParams); +bool HaveAPIWithThisName(const std::string &strMethod); #endif // BITCOIN_RPCCLIENT_H diff --git a/src/rpc/rpcdebug.cpp b/src/rpc/rpcdebug.cpp index 64451723..a8c42cea 100644 --- a/src/rpc/rpcdebug.cpp +++ b/src/rpc/rpcdebug.cpp @@ -1,8 +1,8 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "core/init.h" -#include "rpc/rpcutils.h" +#include "rpc/rpcwallet.h" #include "protocol/relay.h" #include "wallet/wallettxs.h" #include "net/net.h" @@ -120,7 +120,7 @@ void mcd_CloseDatabase(mc_Database *m_DB) int64_t mcd_AddRows(mc_Database *m_DB,int key_size,int value_size,int row_count,int64_t prev_rows) { - int err; + int err,value_len; int per_commit_count=1000; int commit_count=row_count/per_commit_count; int64_t total_rows=prev_rows; @@ -137,6 +137,7 @@ int64_t mcd_AddRows(mc_Database *m_DB,int key_size,int value_size,int row_count, { GetRandBytes((unsigned char*)kbuf, key_size); GetRandBytes((unsigned char*)vbuf, value_size); + m_DB->Read(kbuf,key_size,&value_len,0,&err); err=m_DB->Write(kbuf,key_size,vbuf,value_size,MC_OPT_DB_DATABASE_TRANSACTIONAL); if(err) { @@ -221,9 +222,108 @@ double mcd_ReadRows(mc_Database *m_DB,int key_size,int row_count,int read_type) return ta-tb; } +Value mcd_DebugIssueLicenseToken(const Object& params) +{ + string name=mcd_ParamStringValue(params,"name",""); + int multiple=mcd_ParamIntValue(params,"multiple",1); + int64_t quantity=mcd_ParamIntValue(params,"quantity",1); + + CBitcoinAddress from_address(mcd_ParamStringValue(params,"from_address","")); + if (!from_address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + CBitcoinAddress to_address(mcd_ParamStringValue(params,"to_address","")); + if (!to_address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + CBitcoinAddress req_address(mcd_ParamStringValue(params,"req_address","")); + if (!req_address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + + mc_Script *lpScript=mc_gState->m_TmpBuffers->m_RpcScript3; + lpScript->Clear(); + + unsigned char hash[32]; + memset(hash,0xbc,32); + + lpScript->SetAssetGenesis(quantity); + lpScript->AddElement(); + lpScript->SetData(hash,30); + lpScript->AddElement(); + + mc_Script *lpDetailsScript=mc_gState->m_TmpBuffers->m_RpcScript1; + lpDetailsScript->Clear(); + mc_Script *lpDetails=mc_gState->m_TmpBuffers->m_RpcScript2; + lpDetails->Clear(); + + lpDetails->Clear(); + lpDetails->AddElement(); + + CKeyID KeyID; + req_address.GetKeyID(KeyID); + int64_t dummy_int64=0; + int version=20000202; + int protocol=20007; + unsigned int timestamp=mc_TimeNowAsUInt(); + + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_LICENSE_REQUEST_HASH,hash,32); + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_LICENSE_REQUEST_ADDRESS,(unsigned char*)&KeyID,20); + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_NAME,(const unsigned char*)(name.c_str()),name.size());//+1); + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_ASSET_MULTIPLE,(unsigned char*)&multiple,4); + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_LICENSE_CONFIRMATION_TIME,(unsigned char*)&dummy_int64,4); + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_LICENSE_CONFIRMATION_REF,(unsigned char*)&dummy_int64,2); + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_LICENSE_PUBKEY,(unsigned char*)&dummy_int64,2); + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_LICENSE_MIN_VERSION,(unsigned char*)&version,4); + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_LICENSE_MIN_PROTOCOL,(unsigned char*)&protocol,4); + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_LICENSE_SIGNATURE,(unsigned char*)&dummy_int64,1); + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_TIMESTAMP,(unsigned char*)×tamp,4); + + int err; + size_t bytes; + const unsigned char *script; + size_t elem_size; + const unsigned char *elem; + CScript scriptOpReturn=CScript(); + + + vector addresses; + vector fromaddresses; + + + script=lpDetails->GetData(0,&bytes); + + lpDetailsScript->Clear(); + + err=lpDetailsScript->SetNewEntityType(MC_ENT_TYPE_LICENSE_TOKEN,0,script,bytes); + if(err) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid custom fields, too long"); + } + + elem = lpDetailsScript->GetData(0,&elem_size); + scriptOpReturn << vector(elem, elem + elem_size) << OP_DROP << OP_RETURN; + + + + addresses.push_back(to_address.Get()); + fromaddresses.push_back(from_address.Get()); + CWalletTx wtx; + + EnsureWalletIsUnlocked(); + + { + LOCK (pwalletMain->cs_wallet_send); + + SendMoneyToSeveralAddresses(addresses, 0, wtx, lpScript, scriptOpReturn,fromaddresses); + } + + return wtx.GetHash().GetHex(); +} Value mcd_DebugRequest(string method,const Object& params) { + if(method == "issuelicensetoken") + { + return mcd_DebugIssueLicenseToken(params); + } if(method == "dbopen") { mc_Database *m_DB; @@ -379,6 +479,9 @@ Value debug(const Array& params, bool fHelp) mc_ChunkCollector collector; bool res_found=false; + delay=1000; + attempts=10; + if(params[0].type() == str_type) { if(params[0].get_str() == "findaddress") diff --git a/src/rpc/rpcdump.cpp b/src/rpc/rpcdump.cpp index aec2c379..83d765ff 100644 --- a/src/rpc/rpcdump.cpp +++ b/src/rpc/rpcdump.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "structs/base58.h" diff --git a/src/rpc/rpcexchange.cpp b/src/rpc/rpcexchange.cpp index b30f9860..46110ba4 100644 --- a/src/rpc/rpcexchange.cpp +++ b/src/rpc/rpcexchange.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. diff --git a/src/rpc/rpcfilters.cpp b/src/rpc/rpcfilters.cpp new file mode 100644 index 00000000..97e4842c --- /dev/null +++ b/src/rpc/rpcfilters.cpp @@ -0,0 +1,1155 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2014-2016 The Bitcoin Core developers +// Original code was distributed under the MIT software license. +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + + +#include "rpc/rpcwallet.h" +#include "filters/multichainfilter.h" +#include "filters/filter.h" + +void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry); +void parseStreamIdentifier(Value stream_identifier,mc_EntityDetails *entity); + +void ParseFilterRestrictionsForField(Value param,mc_Script *lpDetailsScript,uint32_t filter_type) +{ + lpDetailsScript->Clear(); + lpDetailsScript->AddElement(); + vector inputStrings; + if(param.type() == str_type) + { + inputStrings.push_back(param.get_str()); + } + else + { + inputStrings=ParseStringList(param); + } + for(int is=0;is<(int)inputStrings.size();is++) + { + mc_EntityDetails entity; + ParseEntityIdentifier(inputStrings[is],&entity, MC_ENT_TYPE_ANY); + if(entity.GetEntityType() > MC_ENT_TYPE_STREAM_MAX) + { + throw JSONRPCError(RPC_NOT_ALLOWED, "Filter can be created only for streams and assets"); + } + lpDetailsScript->SetData(entity.GetShortRef(),MC_AST_SHORT_TXID_SIZE); + } +} + +void ParseFilterRestrictions(Value param,mc_Script *lpDetailsScript,uint32_t filter_type) +{ + bool field_parsed,already_found; + + lpDetailsScript->Clear(); + lpDetailsScript->AddElement(); + if(param.type() == obj_type) + { + Object objParams = param.get_obj(); + already_found=false; + BOOST_FOREACH(const Pair& s, objParams) + { + field_parsed=false; + if(s.name_ == "for") + { + if(filter_type != MC_FLT_TYPE_TX) + { + throw JSONRPCError(RPC_NOT_ALLOWED,"for field is allowed only for tx filters"); + } + if(already_found) + { + throw JSONRPCError(RPC_INVALID_PARAMETER,"for field can appear only once in the object"); + } + ParseFilterRestrictionsForField(s.value_,lpDetailsScript,filter_type); + field_parsed=true; + already_found=true; + } + if(!field_parsed) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid field: %s",s.name_.c_str())); + } + } + } + else + { + if(param.type() == bool_type) + { + if(param.get_bool()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid restrictions, should be object or boolean false"); + } + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid restrictions, should be object or boolean false"); + } + } +} + +string ParseFilterDetails(Value param) +{ + if(param.type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid filter code, expecting string"); + } + + return param.get_str(); +} + +Value createfilterfromcmd(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 5) + throw runtime_error("Help message not found\n"); + + if(mc_gState->m_Features->Filters() == 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "API is not supported with this protocol version."); + } + + uint32_t filter_type=MC_FLT_TYPE_TX; + if (strcmp(params[1].get_str().c_str(),"streamfilter") == 0) + { + filter_type=MC_FLT_TYPE_STREAM; + } + else + { + if (strcmp(params[1].get_str().c_str(),"txfilter") ) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid entity type, should be txfilter or streamfilter"); + } + + } + + CWalletTx wtx; + + mc_Script *lpScript=mc_gState->m_TmpBuffers->m_RpcScript3; + lpScript->Clear(); + mc_Script *lpDetailsScript=mc_gState->m_TmpBuffers->m_RpcScript1; + lpDetailsScript->Clear(); + mc_Script *lpDetails=mc_gState->m_TmpBuffers->m_RpcScript2; + lpDetails->Clear(); + + int ret,type; + string filter_name=""; + string strError=""; + int err; + int errorCode=RPC_INVALID_PARAMETER; + + if (params[2].type() != str_type) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid filter name, should be string"); + + if(!params[2].get_str().empty()) + { + filter_name=params[2].get_str(); + } + + if(filter_name == "*") + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid filter name: *"); + } + + unsigned char buf_a[MC_AST_ASSET_REF_SIZE]; + if(AssetRefDecode(buf_a,filter_name.c_str(),filter_name.size())) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid filter name, looks like a filter reference"); + } + + + if(filter_name.size()) + { + ret=ParseAssetKey(filter_name.c_str(),NULL,NULL,NULL,NULL,&type,MC_ENT_TYPE_ANY); + if(ret != MC_ASSET_KEY_INVALID_NAME) + { + if(type == MC_ENT_KEYTYPE_NAME) + { + throw JSONRPCError(RPC_DUPLICATE_NAME, "Filter, upgrade, stream or asset with this name already exists"); + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid filter name"); + } + } + } + + lpScript->Clear(); + + lpDetails->Clear(); + lpDetails->AddElement(); + if(filter_name.size()) + { + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_NAME,(unsigned char*)(filter_name.c_str()),filter_name.size());//+1); + } + + size_t bytes; + string js; + const unsigned char *script; + CScript scriptOpReturn=CScript(); + + + + string filter_main_name=MC_FLT_MAIN_NAME_TX; + uint32_t permission_needed=MC_PTP_ADMIN | MC_PTP_CREATE; + string permission_text="create or admin"; + if (filter_type == MC_FLT_TYPE_STREAM) + { + filter_main_name=MC_FLT_MAIN_NAME_STREAM; + permission_needed=MC_PTP_CREATE; + permission_text="create"; + } + + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_FILTER_TYPE,(unsigned char*)&filter_type,4); + + ParseFilterRestrictions(params[3],lpDetailsScript,filter_type); + + script = lpDetailsScript->GetData(0,&bytes); + + if(bytes) + { + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_FILTER_RESTRICTIONS,script,bytes); + } + + vector addresses; + + vector fromaddresses; + EnsureWalletIsUnlocked(); + + if(params[0].get_str() != "*") + { + fromaddresses=ParseAddresses(params[0].get_str(),false,false); + + if(fromaddresses.size() != 1) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Single from-address should be specified"); + } + + if( (IsMine(*pwalletMain, fromaddresses[0]) & ISMINE_SPENDABLE) != ISMINE_SPENDABLE ) + { + throw JSONRPCError(RPC_WALLET_ADDRESS_NOT_FOUND, "Private key for from-address is not found in this wallet"); + } + + set thisFromAddresses; + + BOOST_FOREACH(const CTxDestination& fromaddress, fromaddresses) + { + thisFromAddresses.insert(fromaddress); + } + + CPubKey pkey; + if(!pwalletMain->GetKeyFromAddressBook(pkey,permission_needed,&thisFromAddresses)) + { + throw JSONRPCError(RPC_INSUFFICIENT_PERMISSIONS, "from-address doesn't have " + permission_text + " permission"); + } + } + else + { + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + CKeyID keyID; + + if(address.GetKeyID(keyID)) + { + if( IsMine(*pwalletMain, keyID) & ISMINE_SPENDABLE ) + { + if(mc_gState->m_Permissions->CanCreate(NULL,(unsigned char*)(&keyID))) + { + if (filter_type == MC_FLT_TYPE_STREAM) + { + fromaddresses.push_back(keyID); + } + else + { + if( ((permission_needed & MC_PTP_ADMIN) == 0) || + (mc_gState->m_Permissions->CanAdmin(NULL,(unsigned char*)(&keyID)) != 0) ) + { + fromaddresses.push_back(keyID); + } + } + } + } + } + } + + if(fromaddresses.size() == 0) + { + throw JSONRPCError(RPC_INSUFFICIENT_PERMISSIONS, "This wallet doesn't have keys with " + permission_text + " permission"); + } + } + + + + js=ParseFilterDetails(params[4]); + + mc_Filter *worker=new mc_Filter; + + err=pFilterEngine->CreateFilter(js.c_str(),filter_main_name,pMultiChainFilterEngine->m_CallbackNames[filter_type],worker,strError); + delete worker; + if(err) + { + throw JSONRPCError(RPC_INTERNAL_ERROR,"Couldn't create filter"); + } + if(strError.size()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER,strprintf("Couldn't compile filter code: %s",strError.c_str())); + } + + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_FILTER_CODE,(unsigned char*)js.c_str(),js.size()); + + + script=lpDetails->GetData(0,&bytes); + + + size_t elem_size; + const unsigned char *elem; + + lpDetailsScript->Clear(); + err=lpDetailsScript->SetNewEntityType(MC_ENT_TYPE_FILTER,0,script,bytes); + if(err) + { + strError="Invalid custom fields or filter name, too long"; + goto exitlbl; + } + + elem = lpDetailsScript->GetData(0,&elem_size); + scriptOpReturn << vector(elem, elem + elem_size) << OP_DROP << OP_RETURN; + + + { + LOCK (pwalletMain->cs_wallet_send); + + SendMoneyToSeveralAddresses(addresses, 0, wtx, lpScript, scriptOpReturn,fromaddresses); + } + +exitlbl: + + if(strError.size()) + { + throw JSONRPCError(errorCode, strError); + } + + return wtx.GetHash().GetHex(); +} + +Value listfilters(const Array& params, uint32_t filter_type) +{ + Array results; + uint32_t output_level; + + if(mc_gState->m_Features->Filters() == 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "API is not supported with this protocol version."); + } + + bool check_approval=true; + mc_EntityDetails stream_entity; + stream_entity.Zero(); + const unsigned char *lpEntity; + lpEntity=NULL; + + if(filter_type == MC_FLT_TYPE_STREAM) + { + check_approval=false; + if (params.size() > 0 && params[0].type() != null_type && ((params[0].type() != str_type) || (params[0].get_str() !="*" ) ) ) + { + parseStreamIdentifier(params[0],&stream_entity); + check_approval=true; + lpEntity=stream_entity.GetTxID(); + } + } + + + vector inputStrings; + if (params.size() > 1 && params[1].type() != null_type && ((params[1].type() != str_type) || (params[1].get_str() !="*" ) ) ) + { + if(params[1].type() == str_type) + { + inputStrings.push_back(params[1].get_str()); + if(params[1].get_str() == "") + { + return results; + } + } + else + { + inputStrings=ParseStringList(params[1]); + if(inputStrings.size() == 0) + { + return results; + } + } + } + + set filter_list; + + if(inputStrings.size()) + { + { + LOCK(cs_main); + for(int is=0;is<(int)inputStrings.size();is++) + { + string param=inputStrings[is]; + + mc_EntityDetails filter_entity; + ParseEntityIdentifier(param,&filter_entity,MC_ENT_TYPE_FILTER); + if(filter_entity.GetFilterType() != filter_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid filter identifier, wrong filter type."); + } + filter_list.insert(*(uint256*)filter_entity.GetTxID()); + } + } + } + + + bool verbose=false; + if (params.size() > 2) + { + if(paramtobool(params[2])) + { + verbose=true; + } + } + + output_level=0x07; + + if (verbose) + { + output_level=0x27; + } + + int unconfirmed_count=0; + for(int i=0;i<(int)pMultiChainFilterEngine->m_Filters.size();i++) + { + Object entry; + + if((filter_list.size() == 0) || + (filter_list.find(*(uint256*)pMultiChainFilterEngine->m_Filters[i].m_Details.GetTxID()) != filter_list.end()) ) + { + entry=FilterEntry(pMultiChainFilterEngine->m_Filters[i].m_Details.GetTxID(),output_level,filter_type); + if(entry.size()>0) + { + bool take_it=false; + BOOST_FOREACH(const Pair& p, entry) + { + if(p.name_ == "filterref") + { + if(p.value_.type() == str_type) + { + take_it=true; + } + else + { + unconfirmed_count++; + } + } + } + if(take_it) + { + bool valid=pMultiChainFilterEngine->m_Filters[i].m_CreateError.size() == 0; + entry.push_back(Pair("compiled",valid)); + if(check_approval) + { + entry.push_back(Pair("approved",mc_gState->m_Permissions->FilterApproved(lpEntity,&(pMultiChainFilterEngine->m_Filters[i].m_FilterAddress)) !=0 )); + entry.push_back(Pair("address",pMultiChainFilterEngine->m_Filters[i].m_FilterAddress.ToString())); + } + results.push_back(entry); + } + } + } + } + + sort(results.begin(), results.end(), AssetCompareByRef); + + for(int i=0;i<(int)pMultiChainFilterEngine->m_Filters.size();i++) + { + if((filter_list.size() == 0) || + (filter_list.find(*(uint256*)pMultiChainFilterEngine->m_Filters[i].m_Details.GetTxID()) != filter_list.end()) ) + { + Object entry; + + entry=FilterEntry(pMultiChainFilterEngine->m_Filters[i].m_Details.GetTxID(),output_level,filter_type); + if(entry.size()>0) + { + bool take_it=false; + BOOST_FOREACH(const Pair& p, entry) + { + if(p.name_ == "filterref") + { + if(p.value_.type() != str_type) + { + take_it=true; + } + } + } + if(take_it) + { + bool valid=pMultiChainFilterEngine->m_Filters[i].m_CreateError.size() == 0; + entry.push_back(Pair("compiled",valid)); + if(check_approval) + { + entry.push_back(Pair("approved",mc_gState->m_Permissions->FilterApproved(lpEntity,&(pMultiChainFilterEngine->m_Filters[i].m_FilterAddress)) !=0 )); + entry.push_back(Pair("address",pMultiChainFilterEngine->m_Filters[i].m_FilterAddress.ToString())); + } + results.push_back(entry); + } + } + } + } + + if(check_approval) + { + for(int i=0;i<(int)results.size();i++) + { + uint160 filter_address=0; + BOOST_FOREACH(const Pair& p, results[i].get_obj()) + { + if(p.name_ == "address") + { + filter_address.SetHex(p.value_.get_str()); + } + } + results[i].get_obj().pop_back(); + + if(verbose) + { + mc_PermissionDetails *plsRow; + mc_PermissionDetails *plsDet; + mc_PermissionDetails *plsPend; + int flags,consensus,remaining; + Array admins; + Array pending; + + mc_Buffer *permissions=NULL; + permissions=mc_gState->m_Permissions->GetPermissionList(lpEntity,(unsigned char*)&filter_address,MC_PTP_FILTER,permissions); + + if(permissions->GetCount()) + { + plsRow=(mc_PermissionDetails *)(permissions->GetRow(0)); + + flags=plsRow->m_Flags; + consensus=plsRow->m_RequiredAdmins; + mc_Buffer *details; + + if((flags & MC_PFL_HAVE_PENDING) || (consensus>1)) + { + details=mc_gState->m_Permissions->GetPermissionDetails(plsRow); + } + else + { + details=NULL; + } + + if(details) + { + for(int j=0;jGetCount();j++) + { + plsDet=(mc_PermissionDetails *)(details->GetRow(j)); + remaining=plsDet->m_RequiredAdmins; + if(remaining > 0) + { + uint160 addr; + memcpy(&addr,plsDet->m_LastAdmin,sizeof(uint160)); + CKeyID lpKeyID=CKeyID(addr); + admins.push_back(CBitcoinAddress(lpKeyID).ToString()); + } + } + for(int j=0;jGetCount();j++) + { + plsDet=(mc_PermissionDetails *)(details->GetRow(j)); + remaining=plsDet->m_RequiredAdmins; + if(remaining == 0) + { + Object pend_obj; + Array pend_admins; + uint32_t block_from=plsDet->m_BlockFrom; + uint32_t block_to=plsDet->m_BlockTo; + for(int k=j;kGetCount();k++) + { + plsPend=(mc_PermissionDetails *)(details->GetRow(k)); + remaining=plsPend->m_RequiredAdmins; + + if(remaining == 0) + { + if(block_from == plsPend->m_BlockFrom) + { + if(block_to == plsPend->m_BlockTo) + { + uint160 addr; + memcpy(&addr,plsPend->m_LastAdmin,sizeof(uint160)); + CKeyID lpKeyID=CKeyID(addr); + // CKeyID lpKeyID=CKeyID(*(uint160*)((void*)(plsPend->m_LastAdmin))); + pend_admins.push_back(CBitcoinAddress(lpKeyID).ToString()); + plsPend->m_RequiredAdmins=0x01010101; + } + } + } + } + pend_obj.push_back(Pair("approve", block_from < block_to)); + pend_obj.push_back(Pair("admins", pend_admins)); + pend_obj.push_back(Pair("required", (int64_t)(consensus-pend_admins.size()))); + pending.push_back(pend_obj); + } + } + mc_gState->m_Permissions->FreePermissionList(details); + } + else + { + uint160 addr; + memcpy(&addr,plsRow->m_LastAdmin,sizeof(uint160)); + CKeyID lpKeyID=CKeyID(addr); + admins.push_back(CBitcoinAddress(lpKeyID).ToString()); + } + + results[i].get_obj().push_back(Pair("admins", admins)); + results[i].get_obj().push_back(Pair("pending", pending)); + } + else + { + results[i].get_obj().push_back(Pair("admins", admins)); + results[i].get_obj().push_back(Pair("pending", pending)); + } + mc_gState->m_Permissions->FreePermissionList(permissions); + } + } + } + + return results; +} + +Value listtxfilters(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error("Help message not found\n"); + + Array ext_params; + ext_params.push_back("*"); + BOOST_FOREACH(const Value& value, params) + { + ext_params.push_back(value); + } + + return listfilters(ext_params,MC_FLT_TYPE_TX); +} + +Value liststreamfilters(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error("Help message not found\n"); + + Array ext_params; + ext_params.push_back("*"); + BOOST_FOREACH(const Value& value, params) + { + ext_params.push_back(value); + } + + return listfilters(ext_params,MC_FLT_TYPE_STREAM); +} + +Value getfiltercode(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error("Help message not found\n"); + + if(mc_gState->m_Features->Filters() == 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "API is not supported with this protocol version."); + } + + mc_EntityDetails filter_entity; + ParseEntityIdentifier(params[0],&filter_entity,MC_ENT_TYPE_FILTER); + + char *ptr; + size_t value_size; + char filter_code[MC_ENT_MAX_SCRIPT_SIZE+1]; + + ptr=(char *)filter_entity.GetSpecialParam(MC_ENT_SPRM_FILTER_CODE,&value_size); + + if(ptr == NULL) + { + return Value::null; + } + + if(value_size) + { + memcpy(filter_code,ptr,value_size); + + } + filter_code[value_size]=0x00; + + return string(filter_code); +} + +Value getfiltertxid(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + mc_ThrowHelpMessage("getfiltertxid"); + + return pMultiChainFilterEngine->m_TxID.ToString(); +} + +Value setfilterparam(const json_spirit::Array& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + mc_ThrowHelpMessage("setfilterparam"); + + string param_name=params[0].get_str(); + bool fFound=false; + + if( (param_name == "maxshowndata") ) + { + if( (params[1].type() == int_type) || (params[1].type() == str_type) ) + { + int nValue; + if(params[1].type() == int_type) + { + nValue=params[1].get_int(); + } + else + { + nValue=atoi(params[1].get_str().c_str()); + } + pMultiChainFilterEngine->m_Params.m_MaxShownData=nValue; + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter value type"); + } + fFound=true; + } + + if(!fFound) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "Unsupported runtime parameter: " + param_name); + } + + return "Set"; +} + +Value testfilter(const vector & entities,const char *filter_code, Value txparam,int vout_in,uint32_t filter_type) +{ + Object result; + int err; + string strError; + string strReason=""; + int errorCode=RPC_INVALID_PARAMETER; + string strFatal=""; + bool relevant_filter=true; + int64_t nStart; + int vout=vout_in; + string txhex=""; + + CTransaction tx; + + uint256 hash=0; + if(txparam.type() != null_type) + { + if(txparam.type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx-hex, should be string"); + } + txhex=txparam.get_str(); + if(txhex.size() == 0) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Empty tx-hex"); + } + if(txhex.size() <= 64) + { + hash = ParseHashV(txparam, "tx-hex"); + uint256 hashBlock = 0; + if (!GetTransaction(hash, tx, hashBlock, true)) + throw JSONRPCError(RPC_TX_NOT_FOUND, "No information available about transaction"); + } + } + + if(txhex.size()) + { + if(hash == 0) + { + if (!DecodeHexTx(tx,txhex)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + } + + + string filter_main_name=MC_FLT_MAIN_NAME_TX; + if (filter_type == MC_FLT_TYPE_STREAM) + { + filter_main_name=MC_FLT_MAIN_NAME_STREAM; + + if(txhex.size()) + { + int vout_min,vout_max,vout_count; + vout_count=0; + vout_min=0; + vout_max=(int)tx.vout.size()-1; + if(vout >= 0) + { + if(vout >= (int)tx.vout.size()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "vout out of range"); + } + vout_min=vout; + vout_max=vout; + } + + for(int i=vout_min;i<=vout_max;i++) + { + set streams_already_seen; + + Value result=DataItemEntry(tx,i,streams_already_seen, 0x0102); + + if(result.type() == obj_type) + { + vout_count++; + vout=i; + } + } + + if(vout_count != 1) + { + if(vout_in < 0) + { + if(vout_count == 0) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Stream input is not found in this transaction"); + } + throw JSONRPCError(RPC_INVALID_PARAMETER, "More than one stream item is found in this transaction, please specify output number"); + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Stream item is not found in this output"); + } + } + } + } + + mc_Filter *worker=new mc_Filter; +// err=pFilterEngine->CreateFilter(filter_code,filter_main_name,pMultiChainFilterEngine->m_CallbackNames[filter_type],worker,strError); + err=pFilterEngine->CreateFilter(filter_code,filter_main_name,pMultiChainFilterEngine->m_CallbackNames[filter_type],worker,pMultiChainFilterEngine->GetSendTimeout(), strError); + if(err) + { + errorCode=RPC_INTERNAL_ERROR; + strFatal="Couldn't create filter"; + goto exitlbl; + } + if(strError.size()) + { + result.push_back(Pair("compiled", false)); + strReason=strError; + relevant_filter=false; + } + else + { + result.push_back(Pair("compiled", true)); + } + + nStart = GetTimeMicros(); + if(txhex.size()) + { + if(relevant_filter) + { + if(entities.size()) + { + uint160 hash=0; + mc_gState->m_Assets->m_TmpRelevantEntities->Clear(); + mc_gState->m_Assets->m_TmpRelevantEntities->Add(&hash); + + Object result; + set TxEntities; + TxToJSON(tx, 0, result); + + + for(int i=1;im_Assets->m_TmpRelevantEntities->GetCount();i++) + { + uint160 hash=0; + memcpy(&hash,mc_gState->m_Assets->m_TmpRelevantEntities->GetRow(i),MC_AST_SHORT_TXID_SIZE); + if(TxEntities.find(hash) == TxEntities.end()) + { + TxEntities.insert(hash); + } + } + mc_gState->m_Assets->m_TmpRelevantEntities->Clear(); + + relevant_filter=false; + for(int i=0;i<(int)entities.size();i++) + { + if(TxEntities.find(entities[i]) != TxEntities.end()) + { + relevant_filter=true; + } + } + } + } + + Array callbacks; + + strError=""; + if(relevant_filter) + { + err=pMultiChainFilterEngine->RunFilterWithCallbackLog(tx,vout,worker,strError,callbacks); + if(err) + { + errorCode=RPC_INTERNAL_ERROR; + strFatal="Couldn't run filter"; + } + } + + if(strError.size()) + { + result.push_back(Pair("passed", false)); + strReason=strError; + } + else + { + result.push_back(Pair("passed", true)); + } + result.push_back(Pair("callbacks", callbacks)); + } + + if(strReason.size()) + { + result.push_back(Pair("reason", strReason)); + } + else + { + result.push_back(Pair("reason", Value::null)); + } + + if(txhex.size()) + { + result.push_back(Pair("time", ((double)GetTimeMicros()-nStart)/1000000.)); + } + +exitlbl: + + delete worker; + + if(strFatal.size()) + { + throw JSONRPCError(errorCode,strFatal); + } + + return result; +} + +Value runtxfilter(const json_spirit::Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error("Help message not found\n"); + + mc_EntityDetails filter_entity; + ParseEntityIdentifier(params[0],&filter_entity,MC_ENT_TYPE_FILTER); + + if(filter_entity.GetFilterType() != MC_FLT_TYPE_TX) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Wrong filter type"); + } + + char filter_code[MC_ENT_MAX_SCRIPT_SIZE+1]; + std::vector entities; + unsigned char *ptr; + size_t value_size; + string txhex=""; + + ptr=(unsigned char *)filter_entity.GetSpecialParam(MC_ENT_SPRM_FILTER_RESTRICTIONS,&value_size); + + if(ptr) + { + if(value_size % MC_AST_SHORT_TXID_SIZE) + { + throw JSONRPCError(RPC_NOT_ALLOWED, "Specified filter has invalid restriction value"); + } + } + + entities=mc_FillRelevantFilterEntitities(ptr, value_size); + + ptr=(unsigned char *)filter_entity.GetSpecialParam(MC_ENT_SPRM_FILTER_CODE,&value_size); + + if(ptr) + { + memcpy(filter_code,ptr,value_size); + filter_code[value_size]=0x00; + } + +/* + if (params.size() > 1) + { + if(params[1].type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx-hex, should be string"); + } + txhex=params[1].get_str(); + if(txhex.size() == 0) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Empty tx-hex"); + } + } +*/ + return testfilter(entities, (char *)filter_code, (params.size() > 1) ? params[1] : Value::null, -1, MC_FLT_TYPE_TX); +} + +Value testtxfilter(const json_spirit::Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + throw runtime_error("Help message not found\n"); + + size_t bytes; + string js; + const unsigned char *script; + std::vector entities; + string txhex=""; + + uint32_t filter_type=MC_FLT_TYPE_TX; + + mc_Script *lpDetailsScript=mc_gState->m_TmpBuffers->m_RpcScript1; + lpDetailsScript->Clear(); + + ParseFilterRestrictions(params[0],lpDetailsScript,filter_type); + + script = lpDetailsScript->GetData(0,&bytes); + + if(bytes) + { + entities=mc_FillRelevantFilterEntitities(script, bytes); + } + + js=ParseFilterDetails(params[1]); +/* + if (params.size() > 2) + { + if(params[2].type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx-hex, should be string"); + } + txhex=params[2].get_str(); + if(txhex.size() == 0) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Empty tx-hex"); + } + } +*/ + return testfilter(entities, js.c_str(), (params.size() > 2) ? params[2] : Value::null, -1, MC_FLT_TYPE_TX); +} + +Value runstreamfilter(const json_spirit::Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 3) + throw runtime_error("Help message not found\n"); + + mc_EntityDetails filter_entity; + ParseEntityIdentifier(params[0],&filter_entity,MC_ENT_TYPE_FILTER); + + if(filter_entity.GetFilterType() != MC_FLT_TYPE_STREAM) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Wrong filter type"); + } + + char filter_code[MC_ENT_MAX_SCRIPT_SIZE+1]; + std::vector entities; + unsigned char *ptr; + size_t value_size; + string txhex=""; + int vout=-1; + + ptr=(unsigned char *)filter_entity.GetSpecialParam(MC_ENT_SPRM_FILTER_RESTRICTIONS,&value_size); + + if(ptr) + { + if(value_size % MC_AST_SHORT_TXID_SIZE) + { + throw JSONRPCError(RPC_NOT_ALLOWED, "Specified filter has invalid restriction value"); + } + } + + entities=mc_FillRelevantFilterEntitities(ptr, value_size); + + ptr=(unsigned char *)filter_entity.GetSpecialParam(MC_ENT_SPRM_FILTER_CODE,&value_size); + + if(ptr) + { + memcpy(filter_code,ptr,value_size); + filter_code[value_size]=0x00; + } + + if(params.size() > 2) + { + if(params[2].type() != int_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid vout, should be integer"); + } + vout=params[2].get_int(); + } +/* + if (params.size() > 1) + { + if(params[1].type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx-hex, should be string"); + } + + txhex=params[1].get_str(); + if(txhex.size() == 0) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Empty tx-hex"); + } + } +*/ + return testfilter(entities, (char *)filter_code, (params.size() > 1) ? params[1] : Value::null, vout, MC_FLT_TYPE_STREAM); +} + +Value teststreamfilter(const json_spirit::Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error("Help message not found\n"); + + size_t bytes; + string js; + const unsigned char *script; + std::vector entities; + string txhex=""; + int vout=-1; + + uint32_t filter_type=MC_FLT_TYPE_STREAM; + + mc_Script *lpDetailsScript=mc_gState->m_TmpBuffers->m_RpcScript1; + lpDetailsScript->Clear(); + + ParseFilterRestrictions(params[0],lpDetailsScript,filter_type); + + script = lpDetailsScript->GetData(0,&bytes); + + if(bytes) + { + entities=mc_FillRelevantFilterEntitities(script, bytes); + } + + js=ParseFilterDetails(params[1]); + + if(params.size() > 3) + { + if(params[3].type() != int_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid vout, should be integer"); + } + vout=params[3].get_int(); + } + +/* + if (params.size() > 2) + { + if(params[2].type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx-hex, should be string"); + } + txhex=params[2].get_str(); + if(txhex.size() == 0) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Empty tx-hex"); + } + } +*/ + + return testfilter(entities, js.c_str(), (params.size() > 2) ? params[2] : Value::null, vout, MC_FLT_TYPE_STREAM); +} + diff --git a/src/rpc/rpchelp.cpp b/src/rpc/rpchelp.cpp index de09acda..fb5a5382 100644 --- a/src/rpc/rpchelp.cpp +++ b/src/rpc/rpchelp.cpp @@ -1,12 +1,13 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "core/main.h" #include "rpc/rpcserver.h" #include "rpc/rpcutils.h" +#include "community/community.h" std::string HelpRequiringPassphraseWrapper() { @@ -33,6 +34,15 @@ std::string mc_RPCHelpString(std::string strMethod) return strHelp; } +void mc_ThrowHelpMessage(std::string strMethod) +{ + if(pMultiChainFilterEngine->m_TxID != 0) + { + throw JSONRPCError(RPC_INVALID_PARAMS, "Wrong number of parameters"); + //throw JSONRPCError(RPC_MISC_ERROR, mc_RPCHelpString(strMethod)); + } + throw runtime_error("Help message not found\n"); +} void mc_InitRPCHelpMap01() { @@ -214,7 +224,7 @@ void mc_InitRPCHelpMap01() "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" "2. n (numeric, required) vout value\n" - "3. includemempool (boolean, optional) Whether to included the mem pool\n" + "3. includemempool (boolean, optional, default true) Whether to included the mem pool\n" "\nResult:\n" "{\n" " \"bestblock\" : \"hash\", (string) the block hash\n" @@ -269,7 +279,7 @@ void mc_InitRPCHelpMap02() "listassets ( asset-identifier(s) verbose count start )\n" "\nReturns list of defined assets\n" "\nArguments:\n" - "1. \"asset-identifier\" (string, optional) Asset identifier - one of the following: issue txid, asset reference, asset name.\n" + "1. \"asset-identifier\" (string, optional, default=*) Asset identifier - one of: issue txid, asset reference, asset name.\n" " or\n" "1. asset-identifier(s) (array, optional) A json array of asset identifiers \n" "2. verbose (boolean, optional, default=false) If true, returns list of all issue transactions, including follow-ons \n" @@ -286,8 +296,8 @@ void mc_InitRPCHelpMap02() "listpermissions ( \"permission(s)\" address(es) verbose )\n" "\nReturns a list of all permissions which have been explicitly granted to addresses.\n" "\nArguments:\n" - "1. \"permission(s)\" (string, optional) Permission strings, comma delimited. Possible values: " + AllowedPermissions() + ". Default: all. \n" - "2. \"address(es)\" (string, optional, default \"*\") The addresses to retrieve permissions for. \"*\" for all addresses\n" + "1. \"permission(s)\" (string, optional, default=*) Permission strings, comma delimited. Possible values: " + AllowedPermissions() + "\n" + "2. \"address(es)\" (string, optional, default=*) The addresses to retrieve permissions for. \"*\" for all addresses\n" " or\n" "2. address(es) (array, optional) A json array of addresses to return permissions for\n" "3. verbose (boolean, optional, default=false) If true, returns list of pending grants \n" @@ -303,10 +313,10 @@ void mc_InitRPCHelpMap02() "liststreams ( stream-identifier(s) verbose count start )\n" "\nReturns list of defined streams\n" "\nArguments:\n" - "1. \"stream-identifier(s)\" (string, optional, default=*, all streams) Stream identifier - one of the following: issue txid, stream reference, stream name.\n" + "1. \"stream-identifier(s)\" (string, optional, default=*) Stream identifier - one of: create txid, stream reference, stream name.\n" " or\n" "1. stream-identifier(s) (array, optional) A json array of stream identifiers \n" - "2. verbose (boolean, optional, default=false) If true, returns stream list of creators \n" + "2. verbose (boolean, optional, default=false) If true, returns list of stream creators \n" "3. count (number, optional, default=INT_MAX - all) The number of streams to display\n" "4. start (number, optional, default=-count - last) Start from specific stream, 0 based, if negative - from the end\n" "\nResult:\n" @@ -843,7 +853,7 @@ void mc_InitRPCHelpMap05() " or\n" "2. issuemore-details (object, required) A json object with issuemore metadata\n" " {\n" - " \"update\" : asset-identifier (string,required) Asset identifier - one of the following: asset txid, asset reference, asset name.\n" + " \"update\" : asset-identifier (string,required) Asset identifier - one of: issue txid, asset reference, asset name.\n" " \"details\" : (object, optional) a json object with custom fields\n" " {\n" " \"param-name\": \"param-value\" (strings, required) The key is the parameter name, the value is parameter value\n" @@ -865,7 +875,7 @@ void mc_InitRPCHelpMap05() " or\n" "2. publish-new-stream-item (object, required) A json object with stream item\n" " {\n" - " \"for\" : stream-identifier (string,required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + " \"for\" : stream-identifier (string,required) Stream identifier - one of: create txid, stream reference, stream name.\n" " \"key\" : key (string,optional, default: \"\") Item key\n" " \"keys\" : keys (array,optional) Item keys, array of strings\n" " \"data\" : data-hex (string,optional, default: \"\") Data hex string\n" @@ -895,7 +905,7 @@ void mc_InitRPCHelpMap05() "2. approve-upgrade (object, required) A json object with approval details\n" " {\n" " \"approve\" : approve (boolean,required) Approve or disapprove\n" - " \"for\" : upgrade-identifier (string,required) Upgrade identifier - one of the following: upgrade txid, upgrade name.\n" + " \"for\" : upgrade-identifier (string,required) Upgrade identifier - one of: create txid, upgrade name.\n" " }\n" */ "\nResult:\n" @@ -945,7 +955,7 @@ void mc_InitRPCHelpMap05() " { (object) A json object describing follow-on asset issue\n" " \"issuemore\" : \n" " {\n" - " \"asset\" : \"asset-identifier\"(string, required) Asset identifier - one of the following: issue txid. asset reference, asset name.\n" + " \"asset\" : \"asset-identifier\"(string, required) Asset identifier - one of: issue txid. asset reference, asset name.\n" " \"raw\" : n (numeric, required) The asset total amount in raw units \n" " ,...\n" " }\n" @@ -1410,18 +1420,57 @@ void mc_InitRPCHelpMap06() "4 custom-fields (object, required) a json object with custom fields\n" " {\n" " \"protocol-version\": version (numeric, optional) Protocol version to upgrade to\n" - " \"parameter-name\": value (numeric, optional) New value for upgradable parameter, one of the following: \n" + " \"parameter-name\": value (numeric, optional) New value for upgradable parameter, one of: \n" " target-block-time,\n" " maximum-block-size,\n" " max-std-tx-size,\n" " max-std-op-returns-count,\n" " max-std-op-return-size,\n" " max-std-op-drops-count,\n" - " max-std-element-size\n" + " max-std-element-size,\n" + " anyone-can-connect,\n" + " anyone-can-send,\n" + " anyone-can-receive,\n" + " anyone-can-receiveempty,\n" + " anyone-can-issue,\n" + " anyone-can-create,\n" + " anyone-can-activate\n" " \"startblock\": block (numeric, optional, default 0) Block to apply from \n" // " \"param-name\": \"param-value\" (strings, required) The key is the parameter name, the value is parameter value\n" " ,...\n" " }\n" + " or \n" + "1. \"entity-type\" (string, required) txfilter\n" + "2. \"txfilter-name\" (string, required) Tx filter name, if not \"\" should be unique.\n" + "3. restrictions (object, required) A json object with optional filter restrictions\n" + " {\n" + " \"for\": \"entity-identifier\" (string, optional) Asset/stream identifier - one of: create txid, stream reference, stream name.\n" + " or\n" + " \"for\": entity-identifier(s) (array, optional) A json array of asset/stream identifiers.\n" + " }\n" + "4. \"javascript-code\" (string, required) JavaScript filter code, see help filters. Example:\n" + " function filtertransaction()\n" + " {\n" + " var tx=getfiltertransaction();\n" + " \n" + " if (tx.vout.length<1)\n" + " return \"One output required\";\n" + " }\n" + " or \n" + "1. \"entity-type\" (string, required) streamfilter\n" + "2. \"streamfilter-name\" (string, required) Stream filter name, if not \"\" should be unique.\n" + "3. restrictions (object, required) A json object with filter restrictions\n" + " {\n" + " }\n" + "4. \"javascript-code\" (string, required) JavaScript filter code, see help filters. Example:\n" + " function filterstreamitem()\n" + " {\n" + " var item=getfilterstreamitem();\n" + " \n" + " if (item.keys.length<2)\n" + " return \"At least two keys required\";\n" + " } \n" + "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" @@ -1458,18 +1507,59 @@ void mc_InitRPCHelpMap06() "5 custom-fields (object, required) a json object with custom fields\n" " {\n" " \"protocol-version\": version (numeric, optional) Protocol version to upgrade to \n" - " \"parameter-name\": value (numeric, optional) New value for upgradable parameter, one of the following: \n" + " \"parameter-name\": value (numeric, optional) New value for upgradable parameter, one of: \n" " target-block-time,\n" " maximum-block-size,\n" " max-std-tx-size,\n" " max-std-op-returns-count,\n" " max-std-op-return-size,\n" " max-std-op-drops-count,\n" - " max-std-element-size\n" + " max-std-element-size,\n" + " anyone-can-connect,\n" + " anyone-can-send,\n" + " anyone-can-receive,\n" + " anyone-can-receiveempty,\n" + " anyone-can-issue,\n" + " anyone-can-create,\n" + " anyone-can-activate\n" " \"start-block\": block (numeric, optional, default 0) Block to apply from \n" // " \"param-name\": \"param-value\" (strings, required) The key is the parameter name, the value is parameter value\n" " ,...\n" " }\n" + " or \n" + "1. \"from-address\" (string, required) Address used for creating.\n" + "2. \"entity-type\" (string, required) txfilter\n" + "3. \"txfilter-name\" (string, required) Tx filter name, if not \"\" should be unique.\n" + "4. restrictions (object, required) a json object with filter restrictions\n" + " {\n" + " \"for\": \"entity-identifier\" (string, optional) Asset/stream identifier - one of: create txid, stream reference, stream name.\n" + " or\n" + " \"for\": entity-identifier(s) (array, optional) A json array of asset/stream identifiers .\n" + " }\n" + "5. \"javascript-code\" (string, required) JavaScript filter code, see help filters. Example:\n" + " function filtertransaction()\n" + " {\n" + " var tx=getfiltertransaction();\n" + " \n" + " if (tx.vout.length<1)\n" + " return \"One output required\";\n" + " } \n " + " or \n" + "1. \"from-address\" (string, required) Address used for creating.\n" + "2. \"entity-type\" (string, required) streamfilter\n" + "3. \"streamfilter-name\" (string, required) Stream filter name, if not \"\" should be unique.\n" + "4. restrictions (object, required) A json object with filter restrictions\n" + " {\n" + " }\n" + "5. \"javascript-code\" (string, required) JavaScript filter code, see help filters. Example:\n" + " function filterstreamitem()\n" + " {\n" + " var item=getfilterstreamitem();\n" + " \n" + " if (item.keys.length<2)\n" + " return \"At least two keys required\";\n" + " } \n" + "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" "\nExamples:\n" @@ -1758,7 +1848,7 @@ void mc_InitRPCHelpMap08() "getassettransaction \"asset-identifier\" \"txid\" ( verbose )\n" "\nRetrieves a specific transaction txid involving asset.\n" "\nArguments:\n" - "1. \"asset-identifier\" (string, required) Asset identifier - one of the following: asset txid, asset reference, asset name.\n" + "1. \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name.\n" "2. \"txid\" (string, required) The transaction id\n" "3. verbose (boolean, optional, default=false) If true, returns information about item transaction \n" "\nResult:\n" @@ -1798,12 +1888,12 @@ void mc_InitRPCHelpMap08() "getmultibalances ( address(es) assets minconf includeLocked includeWatchonly ) \n" "\nReturns asset balances for specified address\n" "\nArguments:\n" - "1. \"address(es)\" (string, optional) Address(es) to return balance for, comma delimited. Default - all\n" + "1. \"address(es)\" (string, optional, default=*) Address(es) to return balance for, comma delimited\n" " or\n" "1. address(es) (array, optional) A json array of addresses to return balance for\n" - "2. \"asset\" (string) Single asset identifier to return balance for, default \"*\"\n" + "2. \"asset\" (string, optional, default=*) Single asset identifier to return balance for\n" " or\n" - "2. assets (array, optional) Json array of asset identifiers to return balance for\n" + "2. assets (array, optional) A json array of asset identifiers to return balance for\n" "3. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" "4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n" "5. includeLocked (bool, optional, default=false) Also take locked outputs into account\n" @@ -1891,7 +1981,7 @@ void mc_InitRPCHelpMap09() "getstreamitem \"stream-identifier\" \"txid\" ( verbose )\n" "\nReturns stream item.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. \"txid\" (string, required) The transaction id\n" "3. verbose (boolean, optional, default=false) If true, returns information about item transaction \n" "\nResult:\n" @@ -2324,7 +2414,7 @@ void mc_InitRPCHelpMap10() + HelpRequiringPassphraseWrapper() + "\nArguments:\n" "1. \"address\" (string, required) The address to send newly created asset to.\n" - "2. \"asset-identifier\" (string, required) Asset identifier - one of the following: issue txid, asset reference, asset name.\n" + "2. \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name.\n" "3. quantity (numeric, required) The asset total amount in display units. eg. 1234.56\n" "4. native-amount (numeric, optional) native currency amount to send. eg 0.1, Default: minimum-per-output.\n" "5 custom-fields (object, optional) a json object with custom fields\n" @@ -2347,7 +2437,7 @@ void mc_InitRPCHelpMap10() "\nArguments:\n" "1. \"from-address\" (string, required) Address used for issuing.\n" "2. \"to-address\" (string, required) The address to send newly created asset to.\n" - "3. \"asset-identifier\" (string, required) Asset identifier - one of the following: issue txid, asset reference, asset name.\n" + "3. \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name.\n" "4. quantity (numeric, required) The asset total amount in display units. eg. 1234.56\n" "5. native-amount (numeric, optional) native currency amount to send. eg 0.1, Default: minimum-per-output.\n" "6 custom-fields (object, optional) a json object with custom fields\n" @@ -2404,7 +2494,7 @@ void mc_InitRPCHelpMap11() "listaddresses ( address(es) verbose count start ) \n" "\nReturns asset balances for specified address\n" "\nArguments:\n" - "1. \"address(es)\" (string, optional, default *) Address(es) to return information for, comma delimited. Default - all\n" + "1. \"address(es)\" (string, optional, default=*) Address(es) to return information for, comma delimited. Default - all\n" " or\n" "1. address(es) (array, optional) A json array of addresses to return information for\n" "2. verbose (boolean, optional, default=false) If true return more information about address.\n" @@ -2492,7 +2582,7 @@ void mc_InitRPCHelpMap11() "listassettransactions \"asset-identifier\" ( verbose count start local-ordering )\n" "\nLists transactions involving asset.\n" "\nArguments:\n" - "1. \"asset-identifier\" (string, required) Asset identifier - one of the following: asset txid, asset reference, asset name.\n" + "1. \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name.\n" "2. verbose (boolean, optional, default=false) If true, returns information about transaction \n" "3. count (number, optional, default=10) The number of transactions to display\n" "4. start (number, optional, default=-count - last) Start from specific transaction, 0 based, if negative - from the end\n" @@ -2627,7 +2717,7 @@ void mc_InitRPCHelpMap12() "liststreamitems \"stream-identifier\" ( verbose count start local-ordering )\n" "\nReturns stream items.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. verbose (boolean, optional, default=false) If true, returns information about item transaction \n" "3. count (number, optional, default=10) The number of items to display\n" "4. start (number, optional, default=-count - last) Start from specific item, 0 based, if negative - from the end\n" @@ -2645,7 +2735,7 @@ void mc_InitRPCHelpMap12() "liststreamkeyitems \"stream-identifier\" \"key\" ( verbose count start local-ordering )\n" "\nReturns stream items for specific key.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. \"key\" (string, required) Stream key\n" "3. verbose (boolean, optional, default=false) If true, returns information about item transaction \n" "4. count (number, optional, default=10) The number of items to display\n" @@ -2664,7 +2754,7 @@ void mc_InitRPCHelpMap12() "liststreamkeys \"stream-identifier\" ( key(s) verbose count start local-ordering )\n" "\nReturns stream keys.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. \"key\" (string, optional, default=*) Stream key\n" " or\n" "2. key(s) (array, optional) A json array of stream keys \n" @@ -2686,7 +2776,7 @@ void mc_InitRPCHelpMap12() "liststreampublisheritems \"stream-identifier\" \"address\" ( verbose count start local-ordering )\n" "\nReturns stream items for specific publisher.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. \"address\" (string, required) Publisher address\n" "3. verbose (boolean, optional, default=false) If true, returns information about item transaction \n" "4. count (number, optional, default=10) The number of items to display\n" @@ -2705,7 +2795,7 @@ void mc_InitRPCHelpMap12() "liststreampublishers \"stream-identifier\" ( address(es) verbose count start local-ordering )\n" "\nReturns stream publishers.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. \"address(es)\" (string, optional, default=*) Publisher addresses, comma delimited\n" " or\n" "2. address(es) (array, optional) A json array of publisher addresses \n" @@ -2967,7 +3057,7 @@ void mc_InitRPCHelpMap13() "\nPublishes stream item\n" + HelpRequiringPassphraseWrapper() + "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. \"key\" (string, required) Item key\n" " or\n" "2. keys (array, required) Array of item keys\n" @@ -3006,8 +3096,8 @@ void mc_InitRPCHelpMap13() "\nPublishes stream item from specific address\n" + HelpRequiringPassphraseWrapper() + "\nArguments:\n" - "1. \"from-address\" (string, required) Address used for issuing.\n" - "2. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"from-address\" (string, required) Address used for publishsing.\n" + "2. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "3. \"key\" (string, required) Item key\n" " or\n" "3. keys (array, required) Array of item keys\n" @@ -3153,7 +3243,7 @@ void mc_InitRPCHelpMap13() + HelpRequiringPassphraseWrapper() + "\nArguments:\n" "1. \"address\" (string, required) The address to send to.\n" - "2. \"asset-identifier\" (string, required) Asset identifier - one of the following: issue txid, asset reference, asset name.\n" + "2. \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name.\n" "3. asset-qty (numeric, required) Asset quantity to send. eg 0.1\n" "4. native-amount (numeric, optional) native currency amount to send. eg 0.1, Default: minimum-per-output.\n" "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" @@ -3179,7 +3269,7 @@ void mc_InitRPCHelpMap14() + HelpRequiringPassphraseWrapper() + "\nArguments:\n" "1. \"address\" (string, required) The address to send to.\n" - "2. \"asset-identifier\" (string, required) Asset identifier - one of the following: issue txid, asset reference, asset name.\n" + "2. \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name.\n" "3. asset-qty (numeric, required) Asset quantity to send. eg 0.1\n" "4. native-amount (numeric, optional) native currency amount to send. eg 0.1, Default: minimum-per-output.\n" "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" @@ -3202,7 +3292,7 @@ void mc_InitRPCHelpMap14() "\nArguments:\n" "1. \"from-address\" (string, required) Address to send from. \n" "2. \"to-address\" (string, required) The address to send to.\n" - "3. \"asset-identifier\" (string, required) Asset identifier - one of the following: issue txid, asset reference, asset name.\n" + "3. \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name.\n" "4. asset-qty (numeric, required) Asset quantity to send. eg 0.1\n" "5. native-amount (numeric, optional) native currency amount to send. eg 0.1, Default: minimum-per-output.\n" "6. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" @@ -3463,15 +3553,16 @@ void mc_InitRPCHelpMap15() )); mapHelpStrings.insert(std::make_pair("subscribe", - "subscribe entity-identifier(s) ( rescan )\n" + "subscribe entity-identifier(s) ( rescan" + pEF->ENT_TextConstant("help-subscribe-parameters") + " )\n" "\nSubscribes to the stream.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" " or\n" - "1. \"asset-identifier\" (string, required) Asset identifier - one of the following: asset txid, asset reference, asset name.\n" + "1. \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name.\n" " or\n" "1. entity-identifier(s) (array, optional) A json array of stream or asset identifiers \n" "2. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" + + pEF->ENT_TextConstant("help-subscribe-parameters-details") + "\nNote: This call can take minutes to complete if rescan is true.\n" "\nResult:\n" "\nExamples:\n" @@ -3487,9 +3578,9 @@ void mc_InitRPCHelpMap15() "unsubscribe entity-identifier(s) ( purge )\n" "\nUnsubscribes from the stream.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" " or\n" - "1. \"asset-identifier\" (string, required) Asset identifier - one of the following: asset txid, asset reference, asset name.\n" + "1. \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name.\n" " or\n" "1. entity-identifier(s) (array, optional) A json array of stream or asset identifiers \n" "2. purge (boolean, optional, default=false) Purge all offchain data for the stream\n" @@ -3544,7 +3635,7 @@ void mc_InitRPCHelpMap15() "setruntimeparam \"parameter-name\" parameter-value \n" "\nSets value for runtime parameter\n" "\nArguments:\n" - "1. \"parameter-name\" (string, required) Parameter name, one of the following:\n" + "1. \"parameter-name\" (string, required) Parameter name, one of:\n" " miningrequirespeers,\n" " mineemptyrounds,\n" " miningturnover,\n" @@ -3556,6 +3647,9 @@ void mc_InitRPCHelpMap15() " autosubscribe,\n" " handshakelocal,\n" " hideknownopdrops\n" + " acceptfiltertimeout\n" + " sendfiltertimeout\n" + " lockinlinemetadata\n" "2. parameter-value (required) parameter value\n" "\nResult:\n" "\nExamples:\n" @@ -3590,13 +3684,25 @@ void mc_InitRPCHelpMap16() )); mapHelpStrings.insert(std::make_pair("approvefrom", - "approvefrom \"from-address\" \"upgrade-identifier\" ( approve )\n" + "approvefrom \"from-address\" \"upgrade-identifier\"|\"filter-identifier\" ( approve )\n" "\nApprove upgrade using specific address.\n" + HelpRequiringPassphraseWrapper() + "\nArguments:\n" "1. \"from-address\" (string, required) Address used for approval.\n" - "2. \"upgrade-identifier\" (string, required) Upgrade identifier - one of the following: upgrade txid, upgrade name.\n" + "2. \"upgrade-identifier\" (string, required) Upgrade identifier - one of: create txid, upgrade name.\n" "3. approve (boolean, required) Approve or disapprove\n" + " or\n" + "1. \"from-address\" (string, required) Address used for approval.\n" + "2. \"tx-filter-identifier\" (string, required) Tx Filter identifier - one of: create txid, filter reference, filter name.\n" + "3. approve (boolean, required) Approve or disapprove\n" + " or\n" + "1. \"from-address\" (string, required) Address used for approval.\n" + "2. \"stream-filter-identifier\" (string, required) Stream Filter identifier - one of: create txid, filter reference, filter name.\n" + "3. approve (object, required) Approve or disapprove\n" + " {\n" + " \"approve\" : approve (boolean, required) Approve or disapprove\n" + " \"for\" : \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" + " }\n" "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" "\nExamples:\n" @@ -3607,8 +3713,8 @@ void mc_InitRPCHelpMap16() mapHelpStrings.insert(std::make_pair("listupgrades", "listupgrades (upgrade-identifier(s))\n" - "1. \"upgrade-identifier(s)\" (string, optional, default=*, all upgrades) Upgrade identifier - one of the following:\n" - " upgrade txid, upgrade name.\n" + "1. \"upgrade-identifier(s)\" (string, optional, default=*) Upgrade identifier - one of:\n" + " create txid, upgrade name.\n" " or\n" "1. upgrade-identifier(s) (array, optional) A json array of upgrade identifiers \n" "\nReturns list of defined upgrades\n" @@ -3684,7 +3790,7 @@ void mc_InitRPCHelpMap16() "liststreamblockitems \"stream-identifier\" block-set-identifier ( verbose count start )\n" "\nReturns stream items in certain block range.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. \"block-set-identifier\" (string, required) Comma delimited list of block identifiers: \n" " block height,\n" " block hash,\n" @@ -3713,7 +3819,7 @@ void mc_InitRPCHelpMap16() "liststreamtxitems \"stream-identifier\" txids ( verbose )\n" "\nReturns stream items.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. \"txids\" (string, required) Transaction IDs, comma delimited\n" " or\n" "2. txids (array, required) Array of transaction IDs\n" @@ -3761,7 +3867,7 @@ void mc_InitRPCHelpMap16() " or\n" "issuemore-details (object, required) A json object with issuemore metadata\n" " {\n" - " \"update\" : \"asset-identifier\" (string, required) Asset identifier - one of the following: asset txid, asset reference, asset name.\n" + " \"update\" : \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name.\n" " \"details\" : (object, optional) A json object with custom fields\n" " {\n" " \"param-name\": \"param-value\" (strings, required) The key is the parameter name, the value is parameter value\n" @@ -3784,7 +3890,7 @@ void mc_InitRPCHelpMap16() " or\n" "publish-new-stream-item (object, required) A json object with stream item\n" " {\n" - " \"for\" : \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + " \"for\" : \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" " \"options\" : \"options\" (string, optional) Should be \"offchain\" or omitted\n" " \"key\" : \"key\" (string, optional, default: \"\") Item key\n" " or\n" @@ -3821,21 +3927,38 @@ void mc_InitRPCHelpMap16() " \"details\" : (object, optional) A json object with custom fields\n" " {\n" " \"protocol-version\": version (numeric, optional) Protocol version to upgrade to \n" - " \"parameter-name\": value (numeric, optional) New value for upgradable parameter, one of the following: \n" + " \"parameter-name\": value (numeric, optional) New value for upgradable parameter, one of: \n" " target-block-time,\n" " maximum-block-size,\n" " max-std-tx-size,\n" " max-std-op-returns-count,\n" " max-std-op-return-size,\n" " max-std-op-drops-count,\n" - " max-std-element-size\n" + " max-std-element-size,\n" + " anyone-can-connect,\n" + " anyone-can-send,\n" + " anyone-can-receive,\n" + " anyone-can-receiveempty,\n" + " anyone-can-issue,\n" + " anyone-can-create,\n" + " anyone-can-activate\n" " }\n" " }\n" " or\n" "approve-upgrade (object, required) A json object with approval details\n" " {\n" " \"approve\" : approve (boolean, required) Approve or disapprove\n" - " \"for\" : \"upgrade-identifier\" (string, required) Upgrade identifier - one of the following: upgrade txid, upgrade name.\n" + " \"for\" : \"upgrade-identifier\" (string, required) Upgrade identifier - one of: create txid, upgrade name.\n" + " }\n" + " or\n" + "create-new-filter (object, required) A json object with new filter details\n" + " {\n" + " \"create\" : \"filter-type\" (string, required) txfilter or streamfilter\n" + " \"name\" : \"filter-name\" (string, optional) Filter name\n" + " \"for\": \"entity-identifier\" (string, optional) Only for tx filters, Asset/stream identifier - one of: create txid, stream reference, stream name.\n" + " or\n" + " \"for\": entity-identifier(s) (array, optional) Only for tx filters, A json array of asset/stream identifiers .\n" + " \"code\" (string, required) JavaScript filter code, see help filters.\n" " }\n" )); @@ -3860,7 +3983,7 @@ void mc_InitRPCHelpMap16() " or\n" "publish-new-stream-item (object, required) A json object with stream item\n" " {\n" - " \"for\" : \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + " \"for\" : \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" " \"options\" : \"options\" (string, optional) Should be \"offchain\" or omitted\n" " \"key\" : \"key\" (string, optional, default: \"\") Item key\n" " or\n" @@ -3898,7 +4021,7 @@ void mc_InitRPCHelpMap16() " x.xxx (numeric, required) The value is the native currency amount\n" " or \n" " { (object) A json object of assets to send\n" - " \"asset-identifier\" : (string, required) Asset identifier - one of the following: issue txid, asset reference, asset name. \"\" for native currency.\n" + " \"asset-identifier\" : (string, required) Asset identifier - one of: issue txid, asset reference, asset name. \"\" for native currency.\n" " asset-quantity (numeric, required) The asset value. \n" " ,...\n" " }\n" @@ -3913,7 +4036,7 @@ void mc_InitRPCHelpMap16() " { (object) A json object describing follow-on asset issue\n" " \"issuemore\" : \n" " {\n" - " \"asset\" : \"asset-identifier\" (string, required) Asset identifier - one of the following: issue txid. asset reference, asset name.\n" + " \"asset\" : \"asset-identifier\" (string, required) Asset identifier - one of: issue txid. asset reference, asset name.\n" " \"raw\" : n (numeric, required) The asset total amount in raw units \n" " }\n" " }\n" @@ -3923,6 +4046,7 @@ void mc_InitRPCHelpMap16() " {\n" " \"type\" : \"permission(s)\" (string, required) Permission strings, comma delimited. Possible values:\n" " " + AllowedPermissions() + " \n" + " \"for\": \"entity-identifier\" (string, optional) Asset/stream identifier - one of: create txid, stream reference, stream name.\n" " \"startblock\" : n (numeric, optional) Block to apply permissions from (inclusive). Default - 0\n" " \"endblock\" : n (numeric, optional) Block to apply permissions to (exclusive). Default - 4294967295\n" " \"timestamp\" : n (numeric, optional) This helps resolve conflicts between\n" @@ -3954,6 +4078,21 @@ void mc_InitRPCHelpMap16() " ,...\n" "}\n" + " or \n" + "{\n" + " \"tx-filter-identifier\": (string, required) Tx Filter identifier - one of: create txid, filter reference, filter name.\n" + " {\n" + " \"approve\" : approve (boolean, required) Approve or disapprove\n" + " }\n" + "}\n" + " or \n" + "{\n" + " \"stream-filter-identifier\": (string, required) Stream Filter identifier - one of: create txid, filter reference, filter name.\n" + " {\n" + " \"approve\" : approve (boolean, required) Approve or disapprove\n" + " \"for\" : \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" + " }\n" + "}\n" )); } @@ -3964,7 +4103,7 @@ void mc_InitRPCHelpMap17() "getstreamkeysummary \"stream-identifier\" \"key\" \"mode\"\n" "\nReturns stream json object items summary for specific key.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. \"key\" (string, required) Stream key\n" "3. \"mode\" (string, required) Comma delimited list of the following:\n" " jsonobjectmerge (required) - merge json objects\n" @@ -3988,7 +4127,7 @@ void mc_InitRPCHelpMap17() "getstreampublishersummary \"stream-identifier\" \"address\" \"mode\"\n" "\nReturns stream json object items summary for specific publisher.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. \"address\" (string, required) Publisher address\n" "3. \"mode\" (string, required) Comma delimited list of the following:\n" " jsonobjectmerge (required) - merge json objects\n" @@ -4125,7 +4264,7 @@ void mc_InitRPCHelpMap18() "liststreamqueryitems \"stream-identifier\" query ( verbose )\n" "\nReturns stream items for specific query.\n" "\nArguments:\n" - "1. \"stream-identifier\" (string, required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" "2. query (object, required) Query\n" " {\n" " \"key\" : \"key\" (string, optional, default: \"\") Item key\n" @@ -4145,10 +4284,381 @@ void mc_InitRPCHelpMap18() + HelpExampleRpc("liststreamqueryitems", "\"test-stream\", \"{\\\"keys\\\":[\\\"key01\\\",\"key02\"],\\\"publisher\\\":\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\"}\", false") )); - mapHelpStrings.insert(std::make_pair("AAAAAAA", + mapHelpStrings.insert(std::make_pair("listtxfilters", + "listtxfilters ( filter-identifier(s) verbose )\n" + "\nReturns list of defined tx filters\n" + "\nArguments:\n" + "1. \"filter-identifier\" (string, optional, default=*) Filter identifier - one of: create txid, filter reference, filter name.\n" + " or\n" + "1. filter-identifier(s) (array, optional) A json array of filter identifiers \n" + "2. verbose (boolean, optional, default=false) If true, returns list of creators and approval details \n" + "\nResult:\n" + "An array containing list of defined tx filters\n" + "\nExamples:\n" + + HelpExampleCli("listtxfilters", "") + + HelpExampleRpc("listtxfilters", "") + )); + + mapHelpStrings.insert(std::make_pair("liststreamfilters", + "liststreamfilters ( filter-identifier(s) verbose )\n" + "\nReturns list of defined stream filters\n" + "\nArguments:\n" + "1. \"filter-identifier\" (string, optional, default=*) Filter identifier - one of: create txid, filter reference, filter name.\n" + " or\n" + "1. filter-identifier(s) (array, optional) A json array of filter identifiers \n" + "2. verbose (boolean, optional, default=false) If true, returns list of creators and approval details \n" + "\nResult:\n" + "An array containing list of defined stream filters\n" + "\nExamples:\n" + + HelpExampleCli("liststreamfilters", "") + + HelpExampleRpc("liststreamfilters", "") + )); + + mapHelpStrings.insert(std::make_pair("getfiltercode", + "getfiltercode \"filter-identifier\"\n" + "\nReturns code for specified filter\n" + "\nArguments:\n" + "1. \"filter-identifier\" (string, required) Filter identifier - one of: create txid, filter reference, filter name.\n" + "\nResult:\n" + "Filter code in plain text\n" + "\nExamples:\n" + + HelpExampleCli("getfiltercode", "filter1") + + HelpExampleRpc("getfiltercode", "filter1") + )); + + mapHelpStrings.insert(std::make_pair("runtxfilter", + "runtxfilter \"filter-identifier\" ( \"tx-hex\"|\"txid\" )\n" + "\nCompile an existing filter and optionally test it on a transaction\n" + "\nArguments:\n" + "1. \"filter-identifier\" (string, required) Filter identifier - one of: create txid, filter reference, filter name.\n" + "2. \"tx-hex\" (string, optional) The transaction hex string to filter, otherwise filter compiled only\n" + " or\n" + "2. \"txid\" (string, optional) The transaction id\n" + "{\n" + " \"compiled\": true|false, (boolean) Filter passed compilation\n" + " \"passed\": true|false, (boolean) Transaction passed the filter\n" + " \"callbacks\": callbacks, (array of objects) Information about callback calls by filter\n" + " \"reason\": \"rejection reason\", (boolean) Reason for rejection, null if passed\n" + " \"time\": x.xxxxxx, (numeric) Seconds to run transaction through the filter\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("runtxfilter", "filter1") + + HelpExampleRpc("runtxfilter", "filter1") + )); + + mapHelpStrings.insert(std::make_pair("testtxfilter", + "testtxfilter restrictions \"javascript-code\" ( \"tx-hex\"|\"txid\" )\n" + "\nCompile a test filter and optionally test it on a transaction\n" + "\nArguments:\n" + "1. restrictions (object, required) a json object with filter restrictions\n" + " {\n" + " \"for\": \"entity-identifier\" (string, optional) Asset/stream identifier - one of: create txid, stream reference, stream name.\n" + " or\n" + " \"for\": entity-identifier(s) (array, optional) A json array of asset/stream identifiers .\n" + " }\n" + "2. \"javascript-code\" (string, required) JavaScript filter code, see help filters. Example:\n" + " function filtertransaction()\n" + " {\n" + " var tx=getfiltertransaction();\n" + " \n" + " if (tx.vout.length<1)\n" + " return \"One output required\";\n" + " } \n " + "3. \"tx-hex\" (string, optional) The transaction hex string to filter, otherwise filter compiled only\n" + " or\n" + "3. \"txid\" (string, optional) The transaction id\n" + "\nResult:\n" + "{\n" + " \"compiled\": true|false, (boolean) Filter passed compilation\n" + " \"passed\": true|false, (boolean) Transaction passed the filter\n" + " \"callbacks\": callbacks, (array of objects) Information about callback calls by filter\n" + " \"reason\": \"rejection reason\", (boolean) Reason for rejection, null if passed\n" + " \"time\": x.xxxxxx, (numeric) Seconds to run transaction through the filter\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("testtxfilter", "\"{}\" \"javascript-code\"") + + HelpExampleRpc("testtxfilter", "\"{}\", \"javascript-code\"") + )); + + mapHelpStrings.insert(std::make_pair("getfiltertransaction", + "getfiltertransaction\n" + "\nReturns an object with information about the filtered transaction.\n" + )); + + mapHelpStrings.insert(std::make_pair("getfilterstreamitem", + "getfilterstreamitem\n" + "\nReturns an object with information about the filtered stream item.\n" + )); + + mapHelpStrings.insert(std::make_pair("getlastblockinfo", + "getlastblockinfo ( skip )\n" + "\nReturns information about the last or recent blocks in the active chain.\n" + "\nArguments:\n" + "1. skip (numeric, optional) The number of blocks back to skip. Default 0.\n" + "\nResult:\n" + "{\n" + " \"hash\" : \"hash\", (string) The block hash\n" + " \"height\" : n, (numeric) The block height or index\n" + " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"txcount\" : n, (numeric) Number of transactions in block\n" + " \"miner\" : n, (string) The address of the miner\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getlastblockinfo", "") + + HelpExampleRpc("getlastblockinfo", "") + )); + + + +} + +void mc_InitRPCHelpMap19() +{ + mapHelpStrings.insert(std::make_pair("verifypermission", + "verifypermission \"address\" \"permission\" \n" + "\nChecks whether the address has a specified permission.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The address to verify permission for. \n" + "2. \"permission\" (string, required) Permission string. Possible values: " + AllowedPermissions() + ". \n" + "\nResult:\n" + "True if address has specified permission, false otherwise\n" + "\nExamples:\n" + + HelpExampleCli("verifypermission", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" send" ) + + HelpExampleRpc("verifypermission", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", \"send\"") + )); + + mapHelpStrings.insert(std::make_pair("getfiltertxinput", + "getfiltertxinput(n) \n" + "\nReturns details about an unspent transaction output spent in the filtered transaction.\n" + "\nArguments:\n" + "1. vin (numeric, required) Transaction input index\n" + )); + + mapHelpStrings.insert(std::make_pair("getfiltertxid", + "getfiltertxid \n" + "\nReturns the transaction id of the filtered transaction.\n" + )); + + mapHelpStrings.insert(std::make_pair("setfilterparam", + "setfilterparam(\"parameter-name\",parameter-value) \n" + "\nSets value for runtime parameter, used in callbacks while this filter is running\n" + "\nArguments:\n" + "1. \"parameter-name\" (string, required) Parameter name, one of:\n" + " maxshowndata\n" + "2. parameter-value (required) parameter value\n" + )); + mapHelpStrings.insert(std::make_pair("filters", + "\nThe following APIs are allowed to be called from filter code:\n" + " getlastblockinfo \n" + " getassetinfo \n" + " getstreaminfo \n" + " verifypermission \n" + " verifymessage \n" + "\nThe following additional methods can be called from filter code (see help for details):\n" + " getfiltertransaction \n" + " getfiltertxid \n" + " getfilterstreamitem (only for stream filters)\n" + " getfiltertxinput (only for tx filters)\n" + " getfilterassetbalances (only for tx filters)\n" + " setfilterparam \n" + )); + + mapHelpStrings.insert(std::make_pair("getassetinfo", + "getassetinfo \"asset-identifier\" ( verbose )\n" + "\nReturns information about a single asset\n" + "\nArguments:\n" + "1. \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name.\n" + "2. verbose (boolean, optional, default=false) If true, returns list of all issue transactions, including follow-ons \n" + "\nResult:\n" + "Object with asset details\n" + "\nExamples:\n" + + HelpExampleCli("getassetinfo", "myasset") + + HelpExampleRpc("getassetinfo", "myasset") + )); + + mapHelpStrings.insert(std::make_pair("getstreaminfo", + "getstreaminfo \"stream-identifier\" ( verbose )\n" + "\nReturns information about a single stream\n" + "\nArguments:\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name.\n" + "2. verbose (boolean, optional, default=false) If true, returns list of creators \n" + "\nResult:\n" + "Object with stream details\n" + "\nExamples:\n" + + HelpExampleCli("getstreaminfo", "mystream") + + HelpExampleRpc("getstreaminfo", "mystream") + )); + + mapHelpStrings.insert(std::make_pair("runstreamfilter", + "runstreamfilter \"filter-identifier\" ( \"tx-hex\"|\"txid\" vout )\n" + "\nCompile an existing filter and optionally test it on a transaction\n" + "\nArguments:\n" + "1. \"filter-identifier\" (string, required) Filter identifier - one of: create txid, filter reference, filter name.\n" + "2. \"tx-hex\" (string, optional) The transaction hex string to filter, otherwise filter compiled only\n" + " or\n" + "2. \"txid\" (string, optional) The transaction id\n" + "3. vout:n (numeric, optional) The output number, if omitted and txid/tx-hex is specified, found automatically\n" + "\nResult:\n" + "{\n" + " \"compiled\": true|false, (boolean) Filter passed compilation\n" + " \"passed\": true|false, (boolean) Transaction passed the filter\n" + " \"callbacks\": callbacks, (array of objects) Information about callback calls by filter\n" + " \"reason\": \"rejection reason\", (boolean) Reason for rejection, null if passed\n" + " \"time\": x.xxxxxx, (numeric) Seconds to run transaction through the filter\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("runstreamfilter", "filter1") + + HelpExampleRpc("runstreamfilter", "filter1") + )); + + mapHelpStrings.insert(std::make_pair("teststreamfilter", + "teststreamfilter restrictions \"javascript-code\" ( \"tx-hex\"|\"txid\" vout )\n" + "\nCompile a test filter and optionally test it on a transaction\n" + "\nArguments:\n" + "1. restrictions (object, required) a json object with filter restrictions\n" + " {\n" + " }\n" + "2. \"javascript-code\" (string, required) JavaScript filter code, see help filters. Example:\n" + " function filterstreamitem()\n" + " {\n" + " var item=getfilterstreamitem();\n" + " \n" + " if (item.keys.length<2)\n" + " return \"At least two keys required\";\n" + " } \n" + "3. \"tx-hex\" (string, optional) The transaction hex string to filter, otherwise filter compiled only\n" + " or\n" + "3. \"txid\" (string, optional) The transaction id\n" + "4. vout:n (numeric, optional) The output number, if omitted and txid/tx-hex is specified, found automatically\n" + "\nResult:\n" + "{\n" + " \"compiled\": true|false, (boolean) Filter passed compilation\n" + " \"passed\": true|false, (boolean) Transaction passed the filter\n" + " \"callbacks\": callbacks, (array of objects) Information about callback calls by filter\n" + " \"reason\": \"rejection reason\", (boolean) Reason for rejection, null if passed\n" + " \"time\": x.xxxxxx, (numeric) Seconds to run transaction through the filter\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("teststreamfilter", "\"{}\" \"javascript-code\"") + + HelpExampleRpc("teststreamfilter", "\"{}\", \"javascript-code\"") + )); + + mapHelpStrings.insert(std::make_pair("getfilterassetbalances", + "getfilterassetbalances \"asset-identifier\" ( raw )\n" + "\nReturns information about the last or recent blocks in the active chain.\n" + "\nArguments:\n" + "1. \"asset-identifier\" (string, required) Asset identifier - one of: issue txid, asset reference, asset name. \"\" for native currency.\n" + "2. raw (boolean, optional, default false) Return raw value\n" + "\nResult:\n" + "Object with balances for every address involved in transaction\n" + )); + +} + +void mc_InitRPCHelpMap20() +{ + mapHelpStrings.insert(std::make_pair("publishmulti", + "publishmulti \"stream-identifier\" items \"options\" \n" + "\nPublishes several stream items\n" + + HelpRequiringPassphraseWrapper() + + "\nArguments:\n" + "1. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name. Default for items if \"for\" field is omitted\n" + "2. items (array, required) Array of stream items. \n" + " [\n" + " {\n" + " \"for\" : \"stream-identifier\" (string, optional) Stream identifier, uses default if omitted.\n" + " \"options\" : \"options\" (string, optional) Should be \"offchain\" or omitted\n" + " \"key\" : \"key\" (string, optional, default: \"\") Item key\n" + " or\n" + " \"keys\" : keys (array, optional) Item keys, array of strings\n" + " \"data\" : \"data-hex\" (string, optional, default: \"\") Data hex string\n" + " or\n" + " \"data\" : (object, required) JSON data object\n" + " {\n" + " \"json\" : data-json (object, required) Valid JSON string\n" + " }\n" + " or\n" + " \"data\" : (object, required) Text data object\n" + " {\n" + " \"text\" : \"data-text\" (string, required) Data string\n" + " }\n" + " or\n" + " \"data\" (object, required) Binary raw data created with appendbinarycache\n" + " {\n" + " \"cache\" : \"identifier\" (string, required) Binary cache identifier\n" + " }\n" + " }\n" + " ]\n" + "3. \"options\" (string, optional) Should be \"offchain\" or omitted. Default for items if \"options\" field is omitted\n" + "\nResult:\n" + "\"transactionid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("publishmulti", "test [{\"key\":\"hello world\",\"data\":\"48656C6C6F20576F726C64210A\"}]") + + HelpExampleRpc("publishmulti", "\"test\", [{\"key\":\"hello world\",\"data\":\"48656C6C6F20576F726C64210A\"}]") + )); + + mapHelpStrings.insert(std::make_pair("publishmultifrom", + "publishmultifrom \"from-address\" \"stream-identifier\" items \"options\" \n" + "\nPublishes several stream items\n" + + HelpRequiringPassphraseWrapper() + + "\nArguments:\n" + "1. \"from-address\" (string, required) Address used for publishing.\n" + "2. \"stream-identifier\" (string, required) Stream identifier - one of: create txid, stream reference, stream name. Default for items if \"for\" field is omitted\n" + "3. items (array, required) Array of stream items. \n" + " [\n" + " {\n" + " \"for\" : \"stream-identifier\" (string, optional) Stream identifier, uses default if omitted.\n" + " \"options\" : \"options\" (string, optional) Should be \"offchain\" or omitted\n" + " \"key\" : \"key\" (string, optional, default: \"\") Item key\n" + " or\n" + " \"keys\" : keys (array, optional) Item keys, array of strings\n" + " \"data\" : \"data-hex\" (string, optional, default: \"\") Data hex string\n" + " or\n" + " \"data\" : (object, required) JSON data object\n" + " {\n" + " \"json\" : data-json (object, required) Valid JSON string\n" + " }\n" + " or\n" + " \"data\" : (object, required) Text data object\n" + " {\n" + " \"text\" : \"data-text\" (string, required) Data string\n" + " }\n" + " or\n" + " \"data\" (object, required) Binary raw data created with appendbinarycache\n" + " {\n" + " \"cache\" : \"identifier\" (string, required) Binary cache identifier\n" + " }\n" + " }\n" + " ]\n" + "4. \"options\" (string, optional) Should be \"offchain\" or omitted. Default for items if \"options\" field is omitted\n" + "\nResult:\n" + "\"transactionid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("publishmultifrom", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" test [{\"key\":\"hello world\",\"data\":\"48656C6C6F20576F726C64210A\"}]") + + HelpExampleRpc("publishmultifrom", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", \"test\", [{\"key\":\"hello world\",\"data\":\"48656C6C6F20576F726C64210A\"}]") + )); + + mapHelpStrings.insert(std::make_pair("txouttobinarycache", + "txouttobinarycache \"identifier\" \"txid\" vout ( count-bytes start-byte )\n" + "\nStores metadata of transaction output in binary cache.\n" + "\nArguments:\n" + "1. \"identifier\" (string, required) Binary cache item identifier\n" + "2. \"txid\" (string, required) The transaction id\n" + "3. vout (numeric, required) vout value\n" + "4. count-bytes (numeric, optional, default=INT_MAX) Number of bytes to return\n" + "5. start-byte (numeric, optional, default=0) start from specific byte \n" + "\nResult:\n" + "size (numeric) Size of the binary cache item\n" + "\nExamples:\n" + "\nView the data\n" + + HelpExampleCli("txouttobinarycache", "\"TjnVWwHYEg4\" \"txid\" 1") + + "\nAs a json rpc call\n" + + HelpExampleRpc("txouttobinarycache", "\"TjnVWwHYEg4\", \"txid\", 1") + )); + + mapHelpStrings.insert(std::make_pair("AAAAAAA", "" )); - } @@ -4244,6 +4754,8 @@ void mc_InitRPCHelpMap() mc_InitRPCHelpMap16(); mc_InitRPCHelpMap17(); mc_InitRPCHelpMap18(); + mc_InitRPCHelpMap19(); + mc_InitRPCHelpMap20(); mc_InitRPCLogParamCountMap(); mc_InitRPCAllowedWhenWaitingForUpgradeSet(); @@ -4255,5 +4767,25 @@ Value purehelpitem(const Array& params, bool fHelp) if (fHelp) throw runtime_error("Help message not found\n"); + return Value::null; +} + +Value purehelpitem_nomethod(const Array& params, bool fHelp) +{ + if (fHelp) + throw runtime_error("Help message not found\n"); + + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); + + return Value::null; +} + +Value purehelpitem_onlyfilter(const Array& params, bool fHelp) +{ + if (fHelp) + throw runtime_error("Help message not found\n"); + + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This method can only be called from within a filter"); + return Value::null; } \ No newline at end of file diff --git a/src/rpc/rpclist-cold.cpp b/src/rpc/rpclist-cold.cpp index b283e6bc..15070afd 100644 --- a/src/rpc/rpclist-cold.cpp +++ b/src/rpc/rpclist-cold.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #if defined(HAVE_CONFIG_H) diff --git a/src/rpc/rpclist.cpp b/src/rpc/rpclist.cpp index 294e74df..fa3f5caf 100644 --- a/src/rpc/rpclist.cpp +++ b/src/rpc/rpclist.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #if defined(HAVE_CONFIG_H) @@ -77,6 +77,7 @@ static const CRPCCommand vRPCCommands[] = { "blockchain", "getblockchaininfo", &getblockchaininfo, true, false, false }, { "blockchain", "getbestblockhash", &getbestblockhash, true, false, false }, { "blockchain", "getblockcount", &getblockcount, true, false, false }, + { "blockchain", "getlastblockinfo", &getlastblockinfo, true, false, false }, { "blockchain", "getblock", &getblock, true, false, false }, { "blockchain", "getblockhash", &getblockhash, true, false, false }, { "blockchain", "getchaintips", &getchaintips, true, false, false }, @@ -94,7 +95,17 @@ static const CRPCCommand vRPCCommands[] = { "blockchain", "listpermissions", &listpermissions, true, false, false }, { "blockchain", "liststreams", &liststreams, true, false, false }, { "blockchain", "listupgrades", &listupgrades, true, false, false }, + { "blockchain", "listtxfilters", &listtxfilters, true, false, false }, + { "blockchain", "liststreamfilters", &liststreamfilters, true, false, false }, + { "blockchain", "getfiltercode", &getfiltercode, true, false, false }, + { "blockchain", "testtxfilter", &testtxfilter, true, false, false }, + { "blockchain", "runtxfilter", &runtxfilter, true, false, false }, + { "blockchain", "teststreamfilter", &teststreamfilter, true, false, false }, + { "blockchain", "runstreamfilter", &runstreamfilter, true, false, false }, { "blockchain", "listblocks", &listblocks, true, false, false }, + { "blockchain", "getassetinfo", &getassetinfo, true, false, false }, + { "blockchain", "getstreaminfo", &getstreaminfo, true, false, false }, + { "blockchain", "verifypermission", &verifypermission, true, false, false }, /* MCHN END */ /* Mining */ @@ -142,9 +153,17 @@ static const CRPCCommand vRPCCommands[] = { "hidden", "invalidateblock", &invalidateblock, true, true, false }, { "hidden", "reconsiderblock", &reconsiderblock, true, true, false }, { "hidden", "setmocktime", &setmocktime, true, false, false }, - { "hidden", "data-all", &purehelpitem, true, true, true }, - { "hidden", "data-with", &purehelpitem, true, true, true }, - { "hidden", "addresses-all", &purehelpitem, true, true, true }, + { "hidden", "data-all", &purehelpitem_nomethod, true, true, true }, + { "hidden", "data-with", &purehelpitem_nomethod, true, true, true }, + { "hidden", "addresses-all", &purehelpitem_nomethod, true, true, true }, + + { "hidden", "getfilterstreamitem", &purehelpitem_onlyfilter, true, false, false }, + { "hidden", "getfiltertransaction", &purehelpitem_onlyfilter, true, false, false }, + { "hidden", "getfilterassetbalances", &purehelpitem_onlyfilter, true, false, false }, + { "hidden", "getfiltertxid", &purehelpitem_onlyfilter, true, true, true }, + { "hidden", "getfiltertxinput", &purehelpitem_onlyfilter, true, true, true }, + { "hidden", "setfilterparam", &purehelpitem_onlyfilter, true, true, true }, + { "hidden", "filters", &purehelpitem_nomethod, true, true, true }, #ifdef ENABLE_WALLET /* Wallet */ @@ -233,6 +252,8 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "createfrom", &createfromcmd, false, false, true }, { "wallet", "publish", &publish, false, false, true }, { "wallet", "publishfrom", &publishfrom, false, false, true }, + { "wallet", "publishmulti", &publishmulti, false, false, true }, + { "wallet", "publishmultifrom", &publishmultifrom, false, false, true }, { "wallet", "subscribe", &subscribe, false, false, true }, { "wallet", "unsubscribe", &unsubscribe, false, false, true }, { "wallet", "listassettransactions", &listassettransactions, false, false, true }, @@ -246,6 +267,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "liststreamkeys", &liststreamkeys, false, false, true }, { "wallet", "liststreampublishers", &liststreampublishers, false, false, true }, { "wallet", "gettxoutdata", &gettxoutdata, false, false, true }, + { "wallet", "txouttobinarycache", &txouttobinarycache, false, false, true }, { "wallet", "liststreamblockitems", &liststreamblockitems, false, false, false }, { "wallet", "getstreamkeysummary", &getstreamkeysummary, false, false, true }, { "wallet", "getstreampublishersummary", &getstreampublishersummary, false, false, true }, diff --git a/src/rpc/rpcmining.cpp b/src/rpc/rpcmining.cpp index e6789920..74187154 100644 --- a/src/rpc/rpcmining.cpp +++ b/src/rpc/rpcmining.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "structs/amount.h" @@ -15,7 +15,7 @@ #include "rpcserver.h" #include "utils/util.h" #ifdef ENABLE_WALLET -#include "wallet/db.h" +#include "wallet/dbwrap.h" #include "wallet/wallet.h" #endif @@ -193,11 +193,14 @@ Value setgenerate(const Array& params, bool fHelp) } } + int64_t nStart = GetTimeMillis(); + int64_t nCount=1; CBlock *pblock = &pblocktemplate->block; { LOCK(cs_main); /* MCHN START */ IncrementExtraNonce(pblock, pindexPrev, nExtraNonce,pwalletMain); + CreateBlockSignature(pblock,BLOCKSIGHASH_NO_SIGNATURE,pwalletMain); // IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); /* MCHN START */ } @@ -205,7 +208,12 @@ Value setgenerate(const Array& params, bool fHelp) // Yes, there is a chance every nonce could fail to satisfy the -regtest // target -- 1 in 2^(2^32). That ain't gonna happen. ++pblock->nNonce; + CreateBlockSignature(pblock,BLOCKSIGHASH_NO_SIGNATURE,pwalletMain); + nCount++; } + int64_t nEnd = GetTimeMillis(); + LogPrintf("RPC Miner : %ld hashes were tried in %ldms (%8.3fh/ms)\n",nCount,nEnd-nStart, + (nEnd-nStart >0 ) ? (double)nCount/((double)nEnd-(double)nStart) : 0); CValidationState state; LogPrintf("RPC Miner : Block Found - %s, prev: %s, height: %d, txs: %d\n", pblock->GetHash().GetHex(),pblock->hashPrevBlock.ToString().c_str(),nHeight+1,(int)pblock->vtx.size()); diff --git a/src/rpc/rpcmisc.cpp b/src/rpc/rpcmisc.cpp index 358cd151..df827d76 100644 --- a/src/rpc/rpcmisc.cpp +++ b/src/rpc/rpcmisc.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "structs/base58.h" @@ -233,6 +233,9 @@ Value getruntimeparams(const json_spirit::Array& params, bool fHelp) obj.push_back(Pair("lockadminminerounds",Params().LockAdminMineRounds())); obj.push_back(Pair("gen",GetBoolArg("-gen", true))); obj.push_back(Pair("genproclimit",GetArg("-genproclimit", 1))); + obj.push_back(Pair("lockinlinemetadata",GetBoolArg("-lockinlinemetadata", true))); + obj.push_back(Pair("acceptfiltertimeout",GetArg("-acceptfiltertimeout", DEFAULT_ACCEPT_FILTER_TIMEOUT))); + obj.push_back(Pair("sendfiltertimeout",GetArg("-sendfiltertimeout", DEFAULT_SEND_FILTER_TIMEOUT))); /* obj.push_back(Pair("shortoutput",GetBoolArg("-shortoutput",false))); obj.push_back(Pair("walletdbversion", mc_gState->GetWalletDBVersion())); @@ -310,6 +313,11 @@ Value setruntimeparam(const json_spirit::Array& params, bool fHelp) mapArgs ["-" + param_name]=paramtobool(params[1],false) ? "1" : "0"; fFound=true; } + if(param_name == "lockinlinemetadata") + { + mapArgs ["-" + param_name]=paramtobool(params[1],false) ? "1" : "0"; + fFound=true; + } if(param_name == "mineemptyrounds") { if( (params[1].type() == real_type) || (params[1].type() == str_type) ) @@ -362,6 +370,8 @@ Value setruntimeparam(const json_spirit::Array& params, bool fHelp) if( (param_name == "lockadminminerounds") || (param_name == "maxshowndata") || (param_name == "maxqueryscanitems") || + (param_name == "acceptfiltertimeout") || + (param_name == "sendfiltertimeout") || (param_name == "dropmessagestest") ) { if( (params[1].type() == int_type) || (params[1].type() == str_type) ) @@ -378,6 +388,13 @@ Value setruntimeparam(const json_spirit::Array& params, bool fHelp) if( nValue >= 0 ) { mapArgs ["-" + param_name]=strprintf("%d", nValue); + if(param_name == "acceptfiltertimeout") + { + if(pMultiChainFilterEngine) + { + pMultiChainFilterEngine->SetTimeout(pMultiChainFilterEngine->GetAcceptTimeout()); + } + } } else { @@ -639,11 +656,13 @@ Value getblockchainparams(const json_spirit::Array& params, bool fHelp) int n=(int)mc_GetLE(ptr,4); if(n >= 0) { - param_value=((double)n+mc_gState->m_NetworkParams->ParamAccuracy())/MC_PRM_DECIMAL_GRANULARITY; +// param_value=((double)n+mc_gState->m_NetworkParams->ParamAccuracy())/MC_PRM_DECIMAL_GRANULARITY; + param_value=((double)n)/MC_PRM_DECIMAL_GRANULARITY; } else { - param_value=-((double)(-n)+mc_gState->m_NetworkParams->ParamAccuracy())/MC_PRM_DECIMAL_GRANULARITY; +// param_value=-((double)(-n)+mc_gState->m_NetworkParams->ParamAccuracy())/MC_PRM_DECIMAL_GRANULARITY; + param_value=-((double)(-n))/MC_PRM_DECIMAL_GRANULARITY; } } else @@ -654,7 +673,8 @@ Value getblockchainparams(const json_spirit::Array& params, bool fHelp) case MC_PRM_UINT32: if((mc_gState->m_NetworkParams->m_lpParams+i)->m_Type & MC_PRM_DECIMAL) { - param_value=((double)mc_GetLE(ptr,4)+mc_gState->m_NetworkParams->ParamAccuracy())/MC_PRM_DECIMAL_GRANULARITY; +// param_value=((double)mc_GetLE(ptr,4)+mc_gState->m_NetworkParams->ParamAccuracy())/MC_PRM_DECIMAL_GRANULARITY; + param_value=((double)mc_GetLE(ptr,4))/MC_PRM_DECIMAL_GRANULARITY; } else { @@ -722,8 +742,36 @@ Value getblockchainparams(const json_spirit::Array& params, bool fHelp) { param_value=(int)MAX_CHUNK_COUNT; } + if(strcmp("anyonecanconnect",(mc_gState->m_NetworkParams->m_lpParams+i)->m_Name) == 0) + { + param_value=(MCP_ANYONE_CAN_CONNECT != 0); + } + if(strcmp("anyonecansend",(mc_gState->m_NetworkParams->m_lpParams+i)->m_Name) == 0) + { + param_value=(MCP_ANYONE_CAN_SEND != 0); + } + if(strcmp("anyonecanreceive",(mc_gState->m_NetworkParams->m_lpParams+i)->m_Name) == 0) + { + param_value=(MCP_ANYONE_CAN_RECEIVE != 0); + } + if(strcmp("anyonecanreceiveempty",(mc_gState->m_NetworkParams->m_lpParams+i)->m_Name) == 0) + { + param_value=(MCP_ANYONE_CAN_RECEIVE_EMPTY != 0); + } + if(strcmp("anyonecancreate",(mc_gState->m_NetworkParams->m_lpParams+i)->m_Name) == 0) + { + param_value=(MCP_ANYONE_CAN_CREATE != 0); + } + if(strcmp("anyonecanissue",(mc_gState->m_NetworkParams->m_lpParams+i)->m_Name) == 0) + { + param_value=(MCP_ANYONE_CAN_ISSUE != 0); + } + if(strcmp("anyonecanactivate",(mc_gState->m_NetworkParams->m_lpParams+i)->m_Name) == 0) + { + param_value=(MCP_ANYONE_CAN_ACTIVATE != 0); + } } - + if(!hidden) { obj.push_back(Pair(param_name,param_value)); @@ -1104,7 +1152,7 @@ Value createmultisig(const Array& params, bool fHelp) Value verifymessage(const Array& params, bool fHelp) { if (fHelp || params.size() != 3) // MCHN - throw runtime_error("Help message not found\n"); + mc_ThrowHelpMessage("verifymessage"); string strAddress = params[0].get_str(); string strSign = params[1].get_str(); diff --git a/src/rpc/rpcnet.cpp b/src/rpc/rpcnet.cpp index 21477127..749252f9 100644 --- a/src/rpc/rpcnet.cpp +++ b/src/rpc/rpcnet.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "rpc/rpcserver.h" diff --git a/src/rpc/rpcpermissions.cpp b/src/rpc/rpcpermissions.cpp index 8b9dcc25..6437e07d 100644 --- a/src/rpc/rpcpermissions.cpp +++ b/src/rpc/rpcpermissions.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. @@ -90,7 +90,10 @@ Value grantoperation(const Array& params) to=(uint32_t)params[6].get_int64(); } - if(((type & MC_PTP_RECEIVE) == 0) || (from >= to)) + bool require_receive=true; + +/* + if( ((type & MC_PTP_RECEIVE) == 0) || (from >= to)) { if(nAmount > 0) { @@ -103,6 +106,7 @@ Value grantoperation(const Array& params) } } } +*/ timestamp=mc_TimeNowAsUInt(); mc_EntityDetails entity; @@ -113,7 +117,6 @@ Value grantoperation(const Array& params) LogPrintf("mchn: Granting %s permission(s) to address %s (%ld-%ld), Entity TxID: %s, Name: %s\n",permission_type,params[1].get_str(),from,to, ((uint256*)entity.GetTxID())->ToString().c_str(),entity.GetName()); -// type=mc_gState->m_Permissions->GetPermissionType(permission_type.c_str(),entity.GetEntityType()); type=mc_gState->m_Permissions->GetPermissionType(permission_type.c_str(),&entity); if(type == 0) @@ -124,15 +127,34 @@ Value grantoperation(const Array& params) else { type=mc_gState->m_Permissions->GetPermissionType(permission_type.c_str(),&entity); -// type=mc_gState->m_Permissions->GetPermissionType(permission_type.c_str(),MC_ENT_TYPE_NONE); if(type == 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid permission"); + if(type & MC_PTP_RECEIVE) + { + if(from < to) + { + require_receive=false; + } + } + LogPrintf("mchn: Granting %s permission(s) to address %s (%ld-%ld)\n",permission_type,params[1].get_str(),from,to); } - + if(require_receive) + { + if(nAmount > 0) + { + BOOST_FOREACH(CTxDestination& txdest, addresses) + { + if(!AddressCanReceive(txdest)) + { + throw JSONRPCError(RPC_INSUFFICIENT_PERMISSIONS, "Destination address doesn't have receive permission"); + } + } + } + } lpScript->SetPermission(type,from,to,timestamp); @@ -154,6 +176,7 @@ Value grantoperation(const Array& params) { scriptOpReturn=ParseRawMetadata(params[3],MC_DATA_API_PARAM_TYPE_SIMPLE,NULL,&found_entity); } + EnsureWalletIsUnlocked(); if(fromaddresses.size() == 1) { @@ -293,7 +316,6 @@ Value grantoperation(const Array& params) } - EnsureWalletIsUnlocked(); LOCK (pwalletMain->cs_wallet_send); SendMoneyToSeveralAddresses(addresses, nAmount, wtx, lpScript, scriptOpReturn, fromaddresses); @@ -430,6 +452,133 @@ Value revokecmd(const Array& params, bool fHelp) } +Value verifypermission(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + mc_ThrowHelpMessage("verifypermission"); +// throw runtime_error("Help message not found\n"); + + if(params[1].type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid permission, expected string"); + } + + if(params[0].type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid address, expected string"); + } + + uint32_t type; + string entity_identifier, permission_type; + entity_identifier=""; + permission_type="all"; + if (params.size() > 0 && params[1].type() != null_type)// && !params[0].get_str().empty()) + { + permission_type=params[1].get_str(); +// int period_pos=permission_type.find_last_of(".",permission_type.size()); + int period_pos=permission_type.find_last_of("."); + + if(period_pos >= 0) + { + entity_identifier=permission_type.substr(0,period_pos); + permission_type=permission_type.substr(period_pos+1,permission_type.size()); + } + } + + mc_EntityDetails entity; + const unsigned char *lpEntity; + lpEntity=NULL; + entity.Zero(); + if (entity_identifier.size()) + { + ParseEntityIdentifier(entity_identifier,&entity, MC_ENT_TYPE_ANY); + lpEntity=entity.GetTxID(); + } + + type=mc_gState->m_Permissions->GetPermissionType(permission_type.c_str(),&entity); + if(type == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid permission"); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address: "+params[1].get_str()); + + CTxDestination dest=address.Get(); + CKeyID *lpKeyID=boost::get (&dest); + CScriptID *lpScriptID=boost::get (&dest); + + void* lpAddress=NULL; + + if(((lpKeyID == NULL) && (lpScriptID == NULL))) + { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address: "+params[1].get_str()); + return false; + } + + if(lpKeyID != NULL) + { + lpAddress=lpKeyID; + } + else + { + lpAddress=lpScriptID; + } + + + int result=0; + switch(type) + { + case MC_PTP_CONNECT : result = mc_gState->m_Permissions->CanConnectForVerify (lpEntity,lpAddress); break; + case MC_PTP_SEND: result = mc_gState->m_Permissions->CanSend (lpEntity,lpAddress); break; + case MC_PTP_RECEIVE: result = mc_gState->m_Permissions->CanReceive (lpEntity,lpAddress); break; + case MC_PTP_WRITE: result = mc_gState->m_Permissions->CanWrite (lpEntity,lpAddress); break; + case MC_PTP_CREATE: result = mc_gState->m_Permissions->CanCreate (lpEntity,lpAddress); break; + case MC_PTP_ISSUE: result = mc_gState->m_Permissions->CanIssue (lpEntity,lpAddress); break; + case MC_PTP_ACTIVATE: result = mc_gState->m_Permissions->CanActivate (lpEntity,lpAddress); break; + case MC_PTP_MINE: result = mc_gState->m_Permissions->CanMine (lpEntity,lpAddress); break; + case MC_PTP_ADMIN: result = mc_gState->m_Permissions->CanAdmin (lpEntity,lpAddress); break; + case MC_PTP_CUSTOM1: + case MC_PTP_CUSTOM2: + case MC_PTP_CUSTOM3: + case MC_PTP_CUSTOM4: + case MC_PTP_CUSTOM5: + case MC_PTP_CUSTOM6: + result = mc_gState->m_Permissions->CanCustom(lpEntity,lpAddress,type); + break; + default: + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid permission"); + } + + if(result == 0) // No entity restrictions + { + if(entity.GetEntityType()) + { + if(mc_gState->m_Features->FixedIn20005()) + { + if(entity.GetEntityType() == MC_ENT_TYPE_ASSET) + { + if( (entity.Permissions() & type) == 0) + { + result=1; + } + } + else + { + if(entity.GetEntityType() <= MC_ENT_TYPE_STREAM_MAX) + { + if(entity.AnyoneCanWrite()) + { + result=1; + } + } + } + } + } + } + + return (result != 0); +} + Value listpermissions(const Array& params, bool fHelp) { if (fHelp || params.size() > 3) @@ -469,7 +618,6 @@ Value listpermissions(const Array& params, bool fHelp) lpEntity=entity.GetTxID(); } -// type=mc_gState->m_Permissions->GetPermissionType(permission_type.c_str(),entity.GetEntityType()); type=mc_gState->m_Permissions->GetPermissionType(permission_type.c_str(),&entity); if(type == 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid permission"); @@ -580,6 +728,23 @@ Value listpermissions(const Array& params, bool fHelp) case MC_PTP_ACTIVATE :entry.push_back(Pair("type", "activate"));break; default:take_it=false; } + if(!take_it) + { + take_it=true; + if(mc_gState->m_Features->CustomPermissions()) + { + switch(plsRow->m_Type) + { + case MC_PTP_CUSTOM1 :entry.push_back(Pair("type", MC_PTN_CUSTOM1));break; + case MC_PTP_CUSTOM2 :entry.push_back(Pair("type", MC_PTN_CUSTOM2));break; + case MC_PTP_CUSTOM3 :entry.push_back(Pair("type", MC_PTN_CUSTOM3));break; + case MC_PTP_CUSTOM4 :entry.push_back(Pair("type", MC_PTN_CUSTOM4));break; + case MC_PTP_CUSTOM5 :entry.push_back(Pair("type", MC_PTN_CUSTOM5));break; + case MC_PTP_CUSTOM6 :entry.push_back(Pair("type", MC_PTN_CUSTOM6));break; + default:take_it=false; + } + } + } entry.push_back(Pair("startblock", (int64_t)plsRow->m_BlockFrom)); entry.push_back(Pair("endblock", (int64_t)plsRow->m_BlockTo)); if( (plsRow->m_BlockFrom >= plsRow->m_BlockTo) && diff --git a/src/rpc/rpcprotocol.cpp b/src/rpc/rpcprotocol.cpp index 59045275..d95a616a 100644 --- a/src/rpc/rpcprotocol.cpp +++ b/src/rpc/rpcprotocol.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "rpc/rpcprotocol.h" diff --git a/src/rpc/rpcprotocol.h b/src/rpc/rpcprotocol.h index b26d972c..044844a9 100644 --- a/src/rpc/rpcprotocol.h +++ b/src/rpc/rpcprotocol.h @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_RPCPROTOCOL_H @@ -13,8 +13,6 @@ #include #include #include -#include -#include #include "json/json_spirit_reader_template.h" #include "json/json_spirit_utils.h" @@ -101,75 +99,6 @@ enum RPCErrorCode }; -/** - * IOStream device that speaks SSL but can also speak non-SSL - */ -template -class SSLIOStreamDevice : public boost::iostreams::device { -public: - SSLIOStreamDevice(boost::asio::ssl::stream &streamIn, bool fUseSSLIn) : stream(streamIn) - { - fUseSSL = fUseSSLIn; - fNeedHandshake = fUseSSLIn; - } - - void handshake(boost::asio::ssl::stream_base::handshake_type role) - { - if (!fNeedHandshake) return; - fNeedHandshake = false; - stream.handshake(role); - } - std::streamsize read(char* s, std::streamsize n) - { - handshake(boost::asio::ssl::stream_base::server); // HTTPS servers read first - if (fUseSSL) return stream.read_some(boost::asio::buffer(s, n)); - return stream.next_layer().read_some(boost::asio::buffer(s, n)); - } - std::streamsize write(const char* s, std::streamsize n) - { - handshake(boost::asio::ssl::stream_base::client); // HTTPS clients write first - if (fUseSSL) return boost::asio::write(stream, boost::asio::buffer(s, n)); - return boost::asio::write(stream.next_layer(), boost::asio::buffer(s, n)); - } - bool connect(const std::string& server, const std::string& port) - { - using namespace boost::asio::ip; - tcp::resolver resolver(stream.get_io_service()); - tcp::resolver::iterator endpoint_iterator; -#if BOOST_VERSION >= 104300 - try { -#endif - // The default query (flags address_configured) tries IPv6 if - // non-localhost IPv6 configured, and IPv4 if non-localhost IPv4 - // configured. - tcp::resolver::query query(server.c_str(), port.c_str()); - endpoint_iterator = resolver.resolve(query); -#if BOOST_VERSION >= 104300 - } catch(boost::system::system_error &e) - { - // If we at first don't succeed, try blanket lookup (IPv4+IPv6 independent of configured interfaces) - tcp::resolver::query query(server.c_str(), port.c_str(), resolver_query_base::flags()); - endpoint_iterator = resolver.resolve(query); - } -#endif - boost::system::error_code error = boost::asio::error::host_not_found; - tcp::resolver::iterator end; - while (error && endpoint_iterator != end) - { - stream.lowest_layer().close(); - stream.lowest_layer().connect(*endpoint_iterator++, error); - } - if (error) - return false; - return true; - } - -private: - bool fNeedHandshake; - bool fUseSSL; - boost::asio::ssl::stream& stream; -}; - std::string HTTPPost(const std::string& strMsg, const std::map& mapRequestHeaders); std::string HTTPError(int nStatus, bool keepalive, bool headerOnly = false); diff --git a/src/rpc/rpcrawdata.cpp b/src/rpc/rpcrawdata.cpp index 87ced8fb..2692b1a7 100644 --- a/src/rpc/rpcrawdata.cpp +++ b/src/rpc/rpcrawdata.cpp @@ -1,10 +1,12 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "rpc/rpcutils.h" +#include "filters/multichainfilter.h" +#include "filters/filter.h" #include "utils/util.h" #include "json/json_spirit_ubjson.h" @@ -14,11 +16,14 @@ using namespace std; using namespace json_spirit; +void ParseFilterRestrictionsForField(Value param,mc_Script *lpDetailsScript,uint32_t filter_type); + uint32_t ParseRawDataParamType(Value *param,mc_EntityDetails *given_entity,mc_EntityDetails *entity,uint32_t *data_format,int *errorCode,string *strError) { uint32_t param_type=MC_DATA_API_PARAM_TYPE_NONE; uint32_t this_param_type; bool missing_data=true; + bool txfilter=false; *data_format=MC_SCR_DATA_FORMAT_UNKNOWN; entity->Zero(); @@ -47,6 +52,21 @@ uint32_t ParseRawDataParamType(Value *param,mc_EntityDetails *given_entity,mc_En { this_param_type=MC_DATA_API_PARAM_TYPE_CREATE_UPGRADE; } + if(d.value_.get_str() == "txfilter") + { + if( (param_type == MC_DATA_API_PARAM_TYPE_PUBLISH) || + (param_type == MC_DATA_API_PARAM_TYPE_APPROVAL) ) + { + *strError=string("'create' field should preceed 'for'"); + goto exitlbl; + } + this_param_type=MC_DATA_API_PARAM_TYPE_CREATE_FILTER; + txfilter=true; + } + if(d.value_.get_str() == "streamfilter") + { + this_param_type=MC_DATA_API_PARAM_TYPE_CREATE_FILTER; + } } if(this_param_type == MC_DATA_API_PARAM_TYPE_NONE) { @@ -62,29 +82,39 @@ uint32_t ParseRawDataParamType(Value *param,mc_EntityDetails *given_entity,mc_En } if(entity->GetEntityType() != MC_ENT_TYPE_ASSET) { - *strError=string("Asset with this identifier not found"); + *strError=string("Asset with this identifier not found"); + *errorCode=RPC_ENTITY_NOT_FOUND; goto exitlbl; } this_param_type=MC_DATA_API_PARAM_TYPE_FOLLOWON; } if(d.name_ == "for") { - if(d.value_.type() != null_type && !d.value_.get_str().empty()) - { - ParseEntityIdentifier(d.value_,entity, MC_ENT_TYPE_ANY); - } - if(entity->GetEntityType() == MC_ENT_TYPE_STREAM) + if(txfilter) { - this_param_type=MC_DATA_API_PARAM_TYPE_PUBLISH; + param_type=MC_DATA_API_PARAM_TYPE_NONE; + this_param_type=MC_DATA_API_PARAM_TYPE_CREATE_FILTER; } - if(entity->GetEntityType() == MC_ENT_TYPE_UPGRADE) - { - this_param_type=MC_DATA_API_PARAM_TYPE_APPROVAL; - } - if(this_param_type == MC_DATA_API_PARAM_TYPE_NONE) + else { - *strError=string("Entity with this identifier not found"); - goto exitlbl; + if(d.value_.type() != null_type && !d.value_.get_str().empty()) + { + ParseEntityIdentifier(d.value_,entity, MC_ENT_TYPE_ANY); + if(entity->GetEntityType() == MC_ENT_TYPE_STREAM) + { + this_param_type=MC_DATA_API_PARAM_TYPE_PUBLISH; + } + if(entity->GetEntityType() == MC_ENT_TYPE_UPGRADE) + { + this_param_type=MC_DATA_API_PARAM_TYPE_APPROVAL; + } + } + if(this_param_type == MC_DATA_API_PARAM_TYPE_NONE) + { + *strError=string("Entity with this identifier not found"); + *errorCode=RPC_ENTITY_NOT_FOUND; + goto exitlbl; + } } } if( (d.name_ == "text") || (d.name_ == "json") || (d.name_ == "cache") ) @@ -384,7 +414,8 @@ vector ParseRawFormattedData(const Value *value,uint32_t *data_fo { if(*data_format == MC_SCR_DATA_FORMAT_UNKNOWN) { - throw JSONRPCError(RPC_NOT_SUPPORTED, "Unsupported item data type: " + d.name_); + *errorCode=RPC_NOT_SUPPORTED; + *strError="Unsupported item data type: " + d.name_; } } } @@ -711,7 +742,7 @@ CScript RawDataScriptFollowOn(Value *param,mc_EntityDetails *entity,mc_Script *l return scriptOpReturn; } -bool RawDataParseRestrictParameter(const Value& param,uint32_t *restrict,uint32_t *permissions,string *strError) +bool RawDataParseRestrictParameter(const Value& param,uint32_t *restrict,uint32_t *permissions,int *errorCode,string *strError) { *restrict=0; *permissions=0; @@ -747,6 +778,7 @@ bool RawDataParseRestrictParameter(const Value& param,uint32_t *restrict,uint32_ if(match == 0) { *strError="Unsupported restriction"; + *errorCode=RPC_NOT_SUPPORTED; return false; } start=ptr+1; @@ -843,7 +875,7 @@ CScript RawDataScriptCreateStream(Value *param,mc_Script *lpDetails,mc_Script *l { *strError=string("open/restrict field can appear only once in the object"); } - if(RawDataParseRestrictParameter(d.value_,&restrict,&permissions,strError)) + if(RawDataParseRestrictParameter(d.value_,&restrict,&permissions,errorCode,strError)) { if(restrict & MC_ENT_ENTITY_RESTRICTION_OFFCHAIN) { @@ -1049,7 +1081,7 @@ CScript RawDataScriptCreateUpgrade(Value *param,mc_Script *lpDetails,mc_Script * { if(!missing_startblock) { - *strError=string("open field can appear only once in the object"); + *strError=string("startblock field can appear only once in the object"); } if(d.value_.type() == int_type) { @@ -1170,6 +1202,170 @@ CScript RawDataScriptCreateUpgrade(Value *param,mc_Script *lpDetails,mc_Script * return scriptOpReturn; } +CScript RawDataScriptCreateFilter(Value *param,mc_Script *lpDetails,mc_Script *lpDetailsScript,int *errorCode,string *strError) +{ + CScript scriptOpReturn=CScript(); + bool field_parsed; + size_t bytes; + const unsigned char *script; + string entity_name,filter_code,filter_main_name; + uint32_t filter_type=MC_FLT_TYPE_TX; + + bool missing_name=true; + bool missing_code=true; + bool missing_for=true; + + lpDetails->Clear(); + lpDetails->AddElement(); + + lpDetailsScript->Clear(); + lpDetailsScript->AddElement(); + + BOOST_FOREACH(const Pair& d, param->get_obj()) + { + field_parsed=false; + if(d.name_ == "name") + { + if(!missing_name) + { + *strError=string("open field can appear only once in the object"); + } + if(d.value_.type() != null_type && !d.value_.get_str().empty()) + { + entity_name=d.value_.get_str(); + if(entity_name.size()) + { + if(entity_name.size() > MC_ENT_MAX_NAME_SIZE) + { + *strError=string("Invalid filter name - too long"); + } + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_NAME,(const unsigned char*)(entity_name.c_str()),entity_name.size()); + } + } + else + { + *strError=string("Invalid name"); + } + missing_name=false; + field_parsed=true; + } + if(d.name_ == "for") + { + if(!missing_for) + { + *strError=string("for field can appear only once in the object"); + } + + ParseFilterRestrictionsForField(d.value_,lpDetailsScript,MC_FLT_TYPE_TX); + + script = lpDetailsScript->GetData(0,&bytes); + + if(bytes) + { + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_FILTER_RESTRICTIONS,script,bytes); + } + + missing_for=false; + field_parsed=true; + } + + if(d.name_ == "code") + { + if(!missing_code) + { + *strError=string("code field can appear only once in the object"); + } + if(d.value_.type() == str_type) + { + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_FILTER_CODE,(unsigned char*)d.value_.get_str().c_str(),d.value_.get_str().size()); + } + else + { + *strError=string("Invalid code field type"); + } + filter_code=d.value_.get_str(); + missing_code=false; + field_parsed=true; + } + if(d.name_ == "create") + { + if (strcmp(d.value_.get_str().c_str(),"streamfilter") == 0) + { + filter_type=MC_FLT_TYPE_STREAM; + } + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_FILTER_TYPE,(unsigned char*)&filter_type,4); + + + field_parsed=true; + } + if(!field_parsed) + { + *strError=strprintf("Invalid field: %s",d.name_.c_str()); + } + } + + filter_main_name=MC_FLT_MAIN_NAME_TX; + + if(strError->size() == 0) + { + if(filter_type != MC_FLT_TYPE_TX) + { + filter_main_name=MC_FLT_MAIN_NAME_STREAM; + if(!missing_for) + { + *strError=string("for field is allowed only for tx filters"); + *errorCode=RPC_NOT_ALLOWED; + } + } + } + + if(strError->size() == 0) + { + + if(missing_code) + { + *strError=string("Missing code"); + } + else + { + mc_Filter *worker=new mc_Filter; + string strFilterError; + int err=pFilterEngine->CreateFilter(filter_code.c_str(),filter_main_name,pMultiChainFilterEngine->m_CallbackNames[filter_type],worker,strFilterError); + delete worker; + if(err) + { + *strError=string("Couldn't create filter"); + *errorCode=RPC_INTERNAL_ERROR; + } + else + { + if(strFilterError.size()) + { + *strError=strprintf("Couldn't compile filter code: %s",strFilterError.c_str()); + } + } + } + } + + if(strError->size() == 0) + { + int err; + script=lpDetails->GetData(0,&bytes); + lpDetailsScript->Clear(); + err=lpDetailsScript->SetNewEntityType(MC_ENT_TYPE_FILTER,0,script,bytes); + if(err) + { + *strError=string("Invalid code, too long"); + } + else + { + script = lpDetailsScript->GetData(0,&bytes); + scriptOpReturn << vector(script, script + bytes) << OP_DROP << OP_RETURN; + } + } + + return scriptOpReturn; +} CScript RawDataScriptPublish(Value *param,mc_EntityDetails *entity,uint32_t *data_format,mc_Script *lpDetailsScript,vector* vChunkHashes,int *errorCode,string *strError) { @@ -1348,6 +1544,7 @@ CScript RawDataScriptPublish(Value *param,mc_EntityDetails *entity,uint32_t *dat if(out_options & MC_RFD_OPTION_OFFCHAIN) { *strError=string("chunks data type is not allowed with missing options field"); + *errorCode=RPC_NOT_ALLOWED; goto exitlbl; } if(*data_format != MC_SCR_DATA_FORMAT_UNKNOWN) @@ -1579,6 +1776,7 @@ CScript ParseRawMetadata(Value param,uint32_t allowed_objects,mc_EntityDetails * if(param_type != MC_DATA_API_PARAM_TYPE_EMPTY_RAW) { strError=string("Keyword not allowed in this API"); + errorCode=RPC_NOT_ALLOWED; } goto exitlbl; } @@ -1612,6 +1810,9 @@ CScript ParseRawMetadata(Value param,uint32_t allowed_objects,mc_EntityDetails * case MC_DATA_API_PARAM_TYPE_CREATE_UPGRADE: scriptOpReturn=RawDataScriptCreateUpgrade(¶m,lpDetails,lpDetailsScript,&errorCode,&strError); break; + case MC_DATA_API_PARAM_TYPE_CREATE_FILTER: + scriptOpReturn=RawDataScriptCreateFilter(¶m,lpDetails,lpDetailsScript,&errorCode,&strError); + break; case MC_DATA_API_PARAM_TYPE_APPROVAL: scriptOpReturn=RawDataScriptApprove(¶m,&entity,lpDetailsScript,&errorCode,&strError); break; diff --git a/src/rpc/rpcrawtransaction.cpp b/src/rpc/rpcrawtransaction.cpp index 69525455..12f5bba7 100644 --- a/src/rpc/rpcrawtransaction.cpp +++ b/src/rpc/rpcrawtransaction.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "structs/base58.h" @@ -41,6 +41,7 @@ using namespace std; bool OutputCanSend(COutput out); uint32_t mc_CheckSigScriptForMutableTx(const unsigned char *src,int size); +int mc_MaxOpReturnShown(); /* MCHN END */ @@ -58,9 +59,40 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeH if (fIncludeHex) out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); */ - out.push_back(Pair("asm", scriptToShow.ToString())); - if (fIncludeHex) - out.push_back(Pair("hex", HexStr(scriptToShow.begin(), scriptToShow.end()))); + + int max_hex_size=-1; + int script_size=(int)(scriptToShow.end()-scriptToShow.begin()); + + if(mc_gState->m_Features->StreamFilters()) + { + if(pMultiChainFilterEngine->m_TxID != 0) + { + max_hex_size=mc_MaxOpReturnShown(); + } + + if(max_hex_size >= 0) + { + if(script_size <= max_hex_size) + { + max_hex_size=-1; + } + } + } + + if(max_hex_size < 0) + { + out.push_back(Pair("asm", scriptToShow.ToString())); + if (fIncludeHex) + out.push_back(Pair("hex", HexStr(scriptToShow.begin(), scriptToShow.end()))); + } + else + { + Object script_size_object; + script_size_object.push_back(Pair("size", script_size)); + out.push_back(Pair("asm", script_size_object)); + if (fIncludeHex) + out.push_back(Pair("hex", script_size_object)); + } /* MCHN END */ @@ -129,6 +161,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) int chunk_count; int64_t total_chunk_size,out_size; uint32_t retrieve_status; + mc_EntityDetails entity; Array aFormatMetaData; Array aFullFormatMetaData; @@ -288,7 +321,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) if( (mc_GetABRefType(ptr) != MC_AST_ASSET_REF_TYPE_SPECIAL) && (mc_GetABRefType(ptr) != MC_AST_ASSET_REF_TYPE_GENESIS) ) { - mc_EntityDetails entity; + entity.Zero(); if(mc_gState->m_Assets->FindEntityByFullRef(&entity,ptr)) { is_genesis=false; @@ -430,21 +463,21 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) if(is_issuemore) { issue.push_back(Pair("type", "issuemore")); - mc_EntityDetails genesis_entity; + entity.Zero(); unsigned char *ptr; uint256 genesis_hash; - if(mc_gState->m_Assets->FindEntityByShortTxID(&genesis_entity,short_txid)) + if(mc_gState->m_Assets->FindEntityByShortTxID(&entity,short_txid)) { - ptr=(unsigned char *)genesis_entity.GetName(); + ptr=(unsigned char *)entity.GetName(); if(ptr && strlen((char*)ptr)) { issue.push_back(Pair("name", string((char*)ptr))); } - genesis_hash=*(uint256*)genesis_entity.GetTxID(); + genesis_hash=*(uint256*)entity.GetTxID(); issue.push_back(Pair("issuetxid", genesis_hash.GetHex())); - ptr=(unsigned char *)genesis_entity.GetRef(); + ptr=(unsigned char *)entity.GetRef(); string assetref=""; - if(genesis_entity.IsUnconfirmedGenesis()) + if(entity.IsUnconfirmedGenesis()) { Value null_value; issue.push_back(Pair("assetref",null_value)); @@ -471,7 +504,26 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) if(new_entity_type == MC_ENT_TYPE_STREAM) { uint256 txid=tx.GetHash(); - entry.push_back(Pair("create", StreamEntry((unsigned char*)&txid,0x05))); + + mc_EntityDetails *lpEntity=NULL; + entity.Zero(); + if(mc_gState->m_Assets->FindEntityByTxID(&entity,(unsigned char*)&txid) == 0) + { + mc_EntityLedgerRow entity_row; + entity_row.Zero(); + memcpy(entity_row.m_Key,&txid,MC_ENT_KEY_SIZE); + entity_row.m_EntityType=MC_ENT_TYPE_STREAM; + entity_row.m_Block=mc_gState->m_Assets->m_Block; + entity_row.m_Offset=-1; + entity_row.m_ScriptSize=details_script_size; + if(details_script_size) + { + memcpy(entity_row.m_Script,details_script,details_script_size); + } + entity.Set(&entity_row); + lpEntity=&entity; + } + entry.push_back(Pair("create", StreamEntry((unsigned char*)&txid,0x05,lpEntity))); } if(mc_gState->m_Compatibility & MC_VCM_1_0) @@ -495,6 +547,136 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) } } +int VerifyNewTxForStreamFilters(const CTransaction& tx,std::string &strResult,mc_MultiChainFilter **lppFilter,int *applied) +{ + if(mc_gState->m_Features->StreamFilters() == 0) + { + if(GetBoolArg("-sendskipstreamfilters",false)) + { + return MC_ERR_NOERROR; + } + return MC_ERR_NOERROR; + } + + if(pMultiChainFilterEngine->NoStreamFilters()) + { + return MC_ERR_NOERROR; + } + + strResult=""; + for (unsigned int i = 0; i < tx.vout.size(); i++) + { + set streams_already_seen; + bool passed_filters=true; + + Value result=DataItemEntry(tx,i,streams_already_seen, 0x0102); + if(result.type() == obj_type) + { + uint256 hash=*(streams_already_seen.begin()); + + if(pMultiChainFilterEngine) + { + pMultiChainFilterEngine->SetTimeout(pMultiChainFilterEngine->GetSendTimeout()); + } + + int err=pMultiChainFilterEngine->RunStreamFilters(tx,i,(unsigned char*)&hash+MC_AST_SHORT_TXID_OFFSET,-1,0, + strResult,lppFilter,applied); + + if(pMultiChainFilterEngine) + { + pMultiChainFilterEngine->SetTimeout(pMultiChainFilterEngine->GetAcceptTimeout()); + } + if(err != MC_ERR_NOERROR) + { + if(fDebug)LogPrint("mchn","mchn: Stream items rejected (%s): %s\n","Error while running filters",EncodeHexTx(tx)); + passed_filters=false; + } + else + { + if(strResult.size()) + { + if(fDebug)LogPrint("mchn","mchn: Rejecting filter: %s\n",(*lppFilter)->m_FilterCaption.c_str()); + if(fDebug)LogPrint("mchn","mchn: Stream items rejected (%s): %s\n",strResult.c_str(),EncodeHexTx(tx)); + passed_filters=false; + } + } + if(!passed_filters) + { + return MC_ERR_NOT_ALLOWED; + } + } + } + + return MC_ERR_NOERROR; +} + + +Value getfilterstreamitem(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + mc_ThrowHelpMessage("getfilterstreamitem"); +// throw runtime_error("Help message not found\n"); + + if(pMultiChainFilterEngine->m_Vout < 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "This callback cannot be used in tx filters"); + } + + set streams_already_seen; + + if(pMultiChainFilterEngine->m_Vout >= (int)pMultiChainFilterEngine->m_Tx.vout.size()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "vout out of range"); + } + Value result=DataItemEntry(pMultiChainFilterEngine->m_Tx,pMultiChainFilterEngine->m_Vout,streams_already_seen, 0x0E00); + + if(result.type() != obj_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Stream input is not found in this output"); + } + +// result.get_obj().push_back(Pair("txid", pMultiChainFilterEngine->m_Tx.GetHash().GetHex())); + result.get_obj().push_back(Pair("vout", pMultiChainFilterEngine->m_Vout)); + + return result; +} + +Value getfiltertransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + mc_ThrowHelpMessage("getfiltertransaction"); +// throw runtime_error("Help message not found\n"); + +/* + CTransaction tx; + uint256 hashBlock = 0; + if(pMultiChainFilterEngine->m_TxID != 0) + { + if(params.size()) + { + throw JSONRPCError(RPC_INVALID_PARAMS, "TxID parameter should be omitted when called from filter"); + } + tx=pMultiChainFilterEngine->m_Tx; + } + else + { + if(params.size() == 0) + { + throw runtime_error("Help message not found\n"); + } + uint256 hash = ParseHashV(params[0], "parameter 1"); + + + if (!GetTransaction(hash, tx, hashBlock, true)) + throw JSONRPCError(RPC_TX_NOT_FOUND, "No information available about transaction"); + } +*/ + Object result; + TxToJSON(pMultiChainFilterEngine->m_Tx, 0, result); + + return result; +} + Value getrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) // MCHN @@ -1948,6 +2130,15 @@ Value sendrawtransaction(const Array& params, bool fHelp) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); uint256 hashTx = tx.GetHash(); + mc_MultiChainFilter* lpFilter; + int applied=0; + string filter_error=""; + + if(VerifyNewTxForStreamFilters(tx,filter_error,&lpFilter,&applied) == MC_ERR_NOT_ALLOWED) + { + throw JSONRPCError(RPC_NOT_ALLOWED, "Transaction didn't pass stream filter " + lpFilter->m_FilterCaption + ": " + filter_error); + } + bool fOverrideFees = false; if (params.size() > 1) fOverrideFees = params[1].get_bool(); @@ -1960,13 +2151,25 @@ Value sendrawtransaction(const Array& params, bool fHelp) if(mc_gState->m_WalletMode & MC_WMD_ADDRESS_TXS) { - pwalletTxsMain->m_ChunkDB->FlushSourceChunks(GetArg("-flushsourcechunks",true) ? (MC_CDB_FLUSH_MODE_FILE | MC_CDB_FLUSH_MODE_DATASYNC) : MC_CDB_FLUSH_MODE_NONE); + if(pwalletTxsMain->m_ChunkDB->FlushSourceChunks(GetArg("-flushsourcechunks",true) ? (MC_CDB_FLUSH_MODE_FILE | MC_CDB_FLUSH_MODE_DATASYNC) : MC_CDB_FLUSH_MODE_NONE)) + { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't store offchain items, probably chunk database is corrupted"); + } } if (!fHaveMempool && !fHaveChain) { // push to local node and sync with wallets CValidationState state; - if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) { + if(pMultiChainFilterEngine) + { + pMultiChainFilterEngine->SetTimeout(pMultiChainFilterEngine->GetSendTimeout()); + } + bool accepted=AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees); + if(pMultiChainFilterEngine) + { + pMultiChainFilterEngine->SetTimeout(pMultiChainFilterEngine->GetAcceptTimeout()); + } + if (!accepted) { if(state.IsInvalid()) throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); else diff --git a/src/rpc/rpcserver.cpp b/src/rpc/rpcserver.cpp index 42870ee5..828dd49d 100644 --- a/src/rpc/rpcserver.cpp +++ b/src/rpc/rpcserver.cpp @@ -1,10 +1,11 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "rpc/rpcserver.h" +#include "rpc/rpcasio.h" #include "structs/base58.h" #include "core/init.h" @@ -48,6 +49,10 @@ static boost::asio::io_service::work *rpc_dummy_work = NULL; static std::vector rpc_allow_subnets; //!< List of subnets to allow RPC connections from static std::vector< boost::shared_ptr > rpc_acceptors; +//! Convert boost::asio address to CNetAddr +extern CNetAddr BoostAsioToCNetAddr(boost::asio::ip::address address); + + string JSONRPCRequestForLog(const string& strMethod, const Array& params, const Value& id) { Object request; @@ -666,12 +671,14 @@ void mc_InitRPCListIfLimited() } } -void StartRPCThreads() +void StartRPCThreads(string& strError) { mc_InitRPCList(vStaticRPCCommands,vStaticRPCWalletReadCommands); mc_InitRPCListIfLimited(); tableRPC.initialize(); + strError=""; + rpc_allow_subnets.clear(); rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost @@ -734,12 +741,20 @@ void StartRPCThreads() filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; if (filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); - else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string()); + else + { + LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string()); + strError += strprintf("Missing server certificate file %s\n", pathCertFile.string().c_str()); + } filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); - else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string()); + else + { + LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string()); + strError += strprintf("Missing server private key file %s\n", pathPKFile.string().c_str()); + } string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str()); @@ -824,10 +839,27 @@ void StartRPCThreads() } rpc_worker_group = new boost::thread_group(); + + +#ifdef MAC_OSX + boost::thread::attributes attrs; + attrs.set_stack_size(8*1024*1024); + + for (int i = 0; i < GetArg("-rpcthreads", 4); i++) + { + boost::thread *lpThread=new thread(attrs,boost::bind(&asio::io_service::run, rpc_io_service)); + rpc_worker_group->add_thread(lpThread); + } +#else for (int i = 0; i < GetArg("-rpcthreads", 4); i++) rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); - +#endif fRPCRunning = true; + + if(strError.size()) + { + strError += "Node may be unable to process API requests.\n"; + } } void StartDummyRPCThread() diff --git a/src/rpc/rpcserver.h b/src/rpc/rpcserver.h index e03f08a1..c44504d2 100644 --- a/src/rpc/rpcserver.h +++ b/src/rpc/rpcserver.h @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_RPCSERVER_H @@ -34,7 +34,7 @@ class AcceptedConnection }; /** Start RPC threads */ -void StartRPCThreads(); +void StartRPCThreads(std::string& strError); /** * Alternative to StartRPCThreads for the GUI, when no server is * used. The RPC thread in this case is only used to handle timeouts. @@ -77,8 +77,6 @@ void RPCTypeCheck(const json_spirit::Object& o, */ void RPCRunLater(const std::string& name, boost::function func, int64_t nSeconds); -//! Convert boost::asio address to CNetAddr -extern CNetAddr BoostAsioToCNetAddr(boost::asio::ip::address address); typedef json_spirit::Value(*rpcfn_type)(const json_spirit::Array& params, bool fHelp); @@ -129,6 +127,7 @@ extern std::vector vStaticRPCCommands; extern std::vector vStaticRPCWalletReadCommands; void mc_InitRPCHelpMap(); std::string mc_RPCHelpString(std::string strMethod); +void mc_ThrowHelpMessage(std::string strMethod); void mc_InitRPCList(std::vector& vStaticRPCCommands,std::vector& vStaticRPCWalletReadCommands); @@ -248,10 +247,25 @@ extern json_spirit::Value resendwallettransactions(const json_spirit::Array& par extern json_spirit::Value listaddresses(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value liststreams(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listupgrades(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listtxfilters(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value liststreamfilters(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getfiltercode(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getfiltertxid(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value setfilterparam(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value testtxfilter(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value runtxfilter(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value teststreamfilter(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value runstreamfilter(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getassetinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getstreaminfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value verifypermission(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getfiltertxinput(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value createcmd(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value createfromcmd(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value publish(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value publishfrom(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value publishmulti(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value publishmultifrom(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value subscribe(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value unsubscribe(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listassettransactions(const json_spirit::Array& params, bool fHelp); @@ -265,6 +279,7 @@ extern json_spirit::Value liststreampublisheritems(const json_spirit::Array& par extern json_spirit::Value liststreamkeys(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value liststreampublishers(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value gettxoutdata(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value txouttobinarycache(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listblocks(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value liststreamblockitems(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getstreamkeysummary(const json_spirit::Array& params, bool fHelp); @@ -272,6 +287,8 @@ extern json_spirit::Value getstreampublishersummary(const json_spirit::Array& pa extern json_spirit::Value storechunk(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value purehelpitem(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value purehelpitem_nomethod(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value purehelpitem_onlyfilter(const json_spirit::Array& params, bool fHelp); /* MCHN END */ extern json_spirit::Value signmessage(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value verifymessage(const json_spirit::Array& params, bool fHelp); @@ -305,6 +322,9 @@ extern json_spirit::Value getnetworkinfo(const json_spirit::Array& params, bool extern json_spirit::Value setmocktime(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp +extern json_spirit::Value getfiltertransaction(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getfilterstreamitem(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getfilterassetbalances(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value lockunspent(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listlockunspent(const json_spirit::Array& params, bool fHelp); @@ -323,6 +343,7 @@ extern json_spirit::Value getmempoolinfo(const json_spirit::Array& params, bool extern json_spirit::Value getrawmempool(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getblockhash(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getblock(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getlastblockinfo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value gettxoutsetinfo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHelp); diff --git a/src/rpc/rpcstreams.cpp b/src/rpc/rpcstreams.cpp index 9d1fbbcd..917fcd1c 100644 --- a/src/rpc/rpcstreams.cpp +++ b/src/rpc/rpcstreams.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. @@ -9,6 +9,7 @@ #include "json/json_spirit_ubjson.h" #include "json/json_spirit_reader_template.h" #include "json/json_spirit_writer_template.h" +#include "community/community.h" #define MC_QPR_MAX_UNCHECKED_TX_LIST_SIZE 1048576 #define MC_QPR_MAX_MERGED_TX_LIST_SIZE 1024 @@ -21,6 +22,7 @@ Value createupgradefromcmd(const Array& params, bool fHelp); +Value createfilterfromcmd(const Array& params, bool fHelp); void parseStreamIdentifier(Value stream_identifier,mc_EntityDetails *entity) { @@ -95,6 +97,36 @@ void parseStreamIdentifier(Value stream_identifier,mc_EntityDetails *entity) } } +Value getstreaminfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + mc_ThrowHelpMessage("getstreaminfo"); +// throw runtime_error("Help message not found\n"); + + if(params[0].type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stream identifier, expected string"); + } + + uint32_t output_level; + mc_EntityDetails entity; + ParseEntityIdentifier(params[0].get_str(),&entity, MC_ENT_TYPE_STREAM); + + output_level=0x06; + + if (params.size() > 1) + { + if(paramtobool(params[1])) + { + output_level=0x26; + } + } + + + return StreamEntry(entity.GetTxID(),output_level); +} + + Value liststreams(const Array& params, bool fHelp) { if (fHelp || params.size() > 4) @@ -171,7 +203,11 @@ Value liststreams(const Array& params, bool fHelp) { if(paramtobool(params[1])) { - output_level=0x3E; + output_level=0x3E; + if(mc_gState->m_Features->StreamFilters()) + { + output_level |= 0x40; + } } } @@ -317,6 +353,7 @@ Value createstreamfromcmd(const Array& params, bool fHelp) if(mc_gState->m_Features->OffChainData()) { string strError; + int errorCode=RPC_INVALID_PARAMETER; uint32_t permissions=0; uint32_t restrict=0; if(params[3].type() != bool_type) @@ -327,7 +364,7 @@ Value createstreamfromcmd(const Array& params, bool fHelp) { if(d.name_ == "restrict") { - if(RawDataParseRestrictParameter(d.value_,&restrict,&permissions,&strError)) + if(RawDataParseRestrictParameter(d.value_,&restrict,&permissions,&errorCode,&strError)) { if(restrict & MC_ENT_ENTITY_RESTRICTION_OFFCHAIN) { @@ -339,7 +376,7 @@ Value createstreamfromcmd(const Array& params, bool fHelp) } else { - throw JSONRPCError(RPC_INVALID_PARAMETER, strError); + throw JSONRPCError(errorCode, strError); } } else @@ -405,6 +442,7 @@ Value createstreamfromcmd(const Array& params, bool fHelp) vector fromaddresses; CScript scriptOpReturn=CScript(); + EnsureWalletIsUnlocked(); int errorCode=RPC_INVALID_PARAMETER; string strError; lpDetailsScript->Clear(); @@ -487,7 +525,6 @@ Value createstreamfromcmd(const Array& params, bool fHelp) } - EnsureWalletIsUnlocked(); { LOCK (pwalletMain->cs_wallet_send); @@ -518,6 +555,14 @@ Value createfromcmd(const Array& params, bool fHelp) { return createupgradefromcmd(params,fHelp); } + if (strcmp(params[1].get_str().c_str(),"txfilter") == 0) + { + return createfilterfromcmd(params,fHelp); + } + if (strcmp(params[1].get_str().c_str(),"streamfilter") == 0) + { + return createfilterfromcmd(params,fHelp); + } throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid entity type, should be stream"); } @@ -552,6 +597,242 @@ Value publish(const Array& params, bool fHelp) return publishfrom(ext_params,fHelp); } +Value publishmulti(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + throw runtime_error("Help message not found\n"); + + Array ext_params; + ext_params.push_back("*"); + BOOST_FOREACH(const Value& value, params) + { + ext_params.push_back(value); + } + + return publishmultifrom(ext_params,fHelp); +} + +Value publishmultifrom(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 4) + throw runtime_error("Help message not found\n"); + + Array out_params; + + bool from_address_specified=false; + mc_EntityDetails stream_entity; + parseStreamIdentifier(params[1],&stream_entity); + + vector fromaddresses; + set stream_hashes; + set valid_addresses; + set next_addresses; + + string default_options=""; + if(params.size() > 3 ) + { + if(params[3].type() != str_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Stream item options must be offchain or empty"); + } + if( mc_gState->m_Features->OffChainData() == 0 ) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Format options are not supported by this protocol version"); + } + if(params[3].get_str().size()) + { + if(params[3].get_str() == "offchain") + { + default_options = "offchain"; + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Stream item options must be offchain or empty"); + } + } + } + + if(params[0].get_str() != "*") + { + from_address_specified=true; + fromaddresses=ParseAddresses(params[0].get_str(),false,false); + + if(fromaddresses.size() != 1) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Single from-address should be specified"); + } + if( (IsMine(*pwalletMain, fromaddresses[0]) & ISMINE_SPENDABLE) != ISMINE_SPENDABLE ) + { + throw JSONRPCError(RPC_WALLET_ADDRESS_NOT_FOUND, "Private key for from-address is not found in this wallet"); + } + } + + FindAddressesWithPublishPermission(fromaddresses,&stream_entity); + + BOOST_FOREACH(const CTxDestination& from_address, fromaddresses) + { + if( (IsMine(*pwalletMain, from_address) & ISMINE_SPENDABLE) == ISMINE_SPENDABLE ) + { + valid_addresses.insert(from_address); + } + } + + stream_hashes.insert(*(uint256*)stream_entity.GetTxID()); + if(params[2].type() != array_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Items should be array"); + } + + if((int)params[2].get_array().size() > MCP_MAX_STD_OP_RETURN_COUNT) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Number of items exceeds %d (max-std-op-returns-count)",MCP_MAX_STD_OP_RETURN_COUNT)); + } + + Array out_items; + + + BOOST_FOREACH(const Value& data, params[2].get_array()) + { + if(data.type() != obj_type) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Items should be array of objects"); + } + + bool for_found=false; + bool options_found=false; + Object out_item; + + BOOST_FOREACH(const Pair& d, data.get_obj()) + { + if(d.name_ == "for") + { + uint256 hash; + mc_EntityDetails item_entity; + parseStreamIdentifier(d.value_,&item_entity); + hash=*(uint256*)item_entity.GetTxID(); + if(stream_hashes.find(hash) == stream_hashes.end()) + { + stream_hashes.insert(hash); + vector other_addresses; + FindAddressesWithPublishPermission(other_addresses,&item_entity); + next_addresses.clear(); + BOOST_FOREACH(const CTxDestination& other_address, other_addresses) + { + if(valid_addresses.find(other_address) != valid_addresses.end()) + { + if( (IsMine(*pwalletMain, other_address) & ISMINE_SPENDABLE) == ISMINE_SPENDABLE ) + { + next_addresses.insert(other_address); + } + } + } + valid_addresses=next_addresses; + if(valid_addresses.size() == 0) + { + if(from_address_specified) + { + throw JSONRPCError(RPC_INSUFFICIENT_PERMISSIONS, "Publishing in this stream is not allowed from this address."); + } + throw JSONRPCError(RPC_INSUFFICIENT_PERMISSIONS, "This wallet contains no addresses with permission to write to all streams and global send permission."); + } + } + for_found=true; + } + if(d.name_ == "options") + { + options_found=true; + } + } + + out_item=data.get_obj(); + if(!for_found) + { + out_item.push_back(Pair("for",params[1])); + } + if(default_options.size()) + { + if(!options_found) + { + out_item.push_back(Pair("options",default_options)); + } + } + + out_items.push_back(out_item); + } + + CTxDestination out_address; + + if(fromaddresses.size() == 1) + { + out_address=fromaddresses[0]; + } + else + { + set setAddressUints; + set *lpSetAddressUint=NULL; + + BOOST_FOREACH(const CTxDestination& from_address, valid_addresses) + { + const CKeyID *lpKeyID=boost::get (&from_address); + const CScriptID *lpScriptID=boost::get (&from_address); + if(lpKeyID) + { + setAddressUints.insert(*(uint160*)lpKeyID); + } + else + { + if(lpScriptID) + { + setAddressUints.insert(*(uint160*)lpScriptID); + } + } + } + lpSetAddressUint=&setAddressUints; + + uint32_t flags= MC_CSF_ALLOW_SPENDABLE_P2SH | MC_CSF_ALLOWED_COINS_ARE_MINE; + vector vecOutputs; + + pwalletMain->AvailableCoins(vecOutputs, false, NULL, true, true, 0, lpSetAddressUint,flags); + if(vecOutputs.size() == 0) + { + if(from_address_specified) + { + throw JSONRPCError(RPC_INSUFFICIENT_PERMISSIONS, "from-address doesn't have unlocked unspent outputs."); + } + throw JSONRPCError(RPC_WALLET_NO_UNSPENT_OUTPUTS, "Addresses with permission to write to all streams don't have unlocked unspent outputs."); + } + + COutput deepest_coin=vecOutputs[0]; + int max_depth=deepest_coin.nDepth; + + for(int i=1;i<(int)vecOutputs.size();i++) + { + if(vecOutputs[i].nDepth > max_depth) + { + deepest_coin=vecOutputs[i]; + max_depth=deepest_coin.nDepth; + } + } + + CTxOut txout; + deepest_coin.GetHashAndTxOut(txout); + + if (!ExtractDestination(txout.scriptPubKey, out_address)) + { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Addresses with permission to write to all streams don't have unlocked unspent outputs."); + } + } + + Object empty_object; + + out_params.push_back(CBitcoinAddress(out_address).ToString()); + out_params.push_back(empty_object); + out_params.push_back(out_items); + out_params.push_back("send"); + + return createrawsendfrom(out_params,false); +} + Value publishfrom(const Array& params, bool fHelp) { if (fHelp || params.size() < 4 || params.size() > 5) @@ -575,6 +856,7 @@ Value publishfrom(const Array& params, bool fHelp) vector addresses; vector fromaddresses; + EnsureWalletIsUnlocked(); if(params[0].get_str() != "*") { @@ -764,7 +1046,6 @@ Value publishfrom(const Array& params, bool fHelp) lpScript->Clear(); - EnsureWalletIsUnlocked(); LOCK (pwalletMain->cs_wallet_send); SendMoneyToSeveralAddresses(addresses, 0, wtx, lpScript, scriptOpReturn,fromaddresses); @@ -774,7 +1055,7 @@ Value publishfrom(const Array& params, bool fHelp) Value subscribe(const Array& params, bool fHelp) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (fHelp || params.size() < 1 || params.size() > ((pEF->ENT_EditionNumeric() == 0) ? 2 : 3)) throw runtime_error("Help message not found\n"); if((mc_gState->m_WalletMode & MC_WMD_TXS) == 0) @@ -784,9 +1065,22 @@ Value subscribe(const Array& params, bool fHelp) // Whether to perform rescan after import bool fRescan = true; + string indexes="all"; + if (params.size() > 1) - fRescan = params[1].get_bool(); + { + if(params[1].type() == bool_type) + { + fRescan = params[1].get_bool(); + } + } + if (params.size() > 2) + { + pEF->ENT_RPCVerifyEdition(); + indexes=params[2].get_str(); + } + vector inputEntities; vector inputStrings; if(params[0].type() == str_type) @@ -831,6 +1125,11 @@ Value subscribe(const Array& params, bool fHelp) entity.m_EntityType=MC_TET_STREAM_PUBLISHER | MC_TET_TIMERECEIVED; pwalletTxsMain->AddEntity(&entity,MC_EFL_NOT_IN_SYNC); fNewFound=true; + } + entity.m_EntityType=MC_TET_STREAM | MC_TET_CHAINPOS; + if(pEF->STR_CreateSubscription(&entity,indexes) != MC_ERR_FOUND) + { + fNewFound=true; } } @@ -1128,7 +1427,7 @@ Value liststreamitems(const Array& params, bool fHelp) mc_AdjustStartAndCount(&count,&start,entStat.m_LastPos); Array retArray; - pwalletTxsMain->GetList(&entStat.m_Entity,start+1,count,entity_rows); + CheckWalletError(pwalletTxsMain->GetList(&entStat.m_Entity,start+1,count,entity_rows)); for(int i=0;iGetCount();i++) { @@ -1158,7 +1457,7 @@ void getTxsForBlockRange(vector & txids,mc_TxEntity *entity,int height_ count=last_item-first_item+1; if(count > 0) { - pwalletTxsMain->GetList(entity,first_item,count,entity_rows); + CheckWalletError(pwalletTxsMain->GetList(entity,first_item,count,entity_rows)); mc_TxEntityRow *lpEntTx; uint256 hash; @@ -1282,6 +1581,10 @@ void getSubKeyEntityFromKey(string str,mc_TxEntityStat entStat,mc_TxEntity *enti mc_GetCompoundHash160(&stream_subkey_hash,entStat.m_Entity.m_EntityID,&key_string_hash); memcpy(entity->m_EntityID,&stream_subkey_hash,MC_TDB_ENTITY_ID_SIZE); entity->m_EntityType=entStat.m_Entity.m_EntityType | MC_TET_SUBKEY; + if(pEF->STR_IsIndexSkipped(NULL,&(entStat.m_Entity),entity)) + { + CheckWalletError(MC_ERR_NOT_ALLOWED); + } } void getSubKeyEntityFromPublisher(string str,mc_TxEntityStat entStat,mc_TxEntity *entity) @@ -1317,6 +1620,10 @@ void getSubKeyEntityFromPublisher(string str,mc_TxEntityStat entStat,mc_TxEntity memcpy(entity->m_EntityID,&stream_subkey_hash,MC_TDB_ENTITY_ID_SIZE); entity->m_EntityType=entStat.m_Entity.m_EntityType | MC_TET_SUBKEY; + if(pEF->STR_IsIndexSkipped(NULL,&(entStat.m_Entity),entity)) + { + CheckWalletError(MC_ERR_NOT_ALLOWED); + } } Value getstreamsummary(const Array& params, bool fPublisher) @@ -1441,12 +1748,13 @@ Value getstreamsummary(const Array& params, bool fPublisher) Object obj; int i,n,c,m,err,pcount; bool available; + bool first_item=true; err=MC_ERR_NOERROR; n=pwalletTxsMain->GetListSize(&entity,entStat.m_Generation,NULL); i=0; m=10; - Value result; + Value result=obj; while(iGetList(&entity,entStat.m_Generation,i+1,c,entity_rows); + CheckWalletError(pwalletTxsMain->GetList(&entity,entStat.m_Generation,i+1,c,entity_rows)); } mc_TxEntityRow *lpEntTx; lpEntTx=(mc_TxEntityRow*)entity_rows->GetRow(i % m); @@ -1540,13 +1848,17 @@ Value getstreamsummary(const Array& params, bool fPublisher) { if(available) { - if(i == 0) - { - result=a.value_; - } - else + if(a.value_.type() != null_type) // Returned in case of error { - result=mc_MergeValues(&result,&(a.value_),mode,0,&err); + if(first_item) + { + result=a.value_; + first_item=false; + } + else + { + result=mc_MergeValues(&result,&(a.value_),mode,0,&err); + } } } else @@ -1598,9 +1910,12 @@ Value getstreamsummary(const Array& params, bool fPublisher) } else { - if( (mode & MC_VMM_IGNORE_OTHER) == 0) + if(!first_item) { - err=MC_ERR_INVALID_PARAMETER_VALUE; + if( (mode & MC_VMM_IGNORE_OTHER) == 0) + { + err=MC_ERR_INVALID_PARAMETER_VALUE; + } } obj.push_back(Pair("json", empty_object)); } @@ -1716,7 +2031,7 @@ Value liststreamkeyitems(const Array& params, bool fHelp) mc_AdjustStartAndCount(&count,&start,pwalletTxsMain->GetListSize(&entity,entStat.m_Generation,NULL)); Array retArray; - pwalletTxsMain->GetList(&entity,entStat.m_Generation,start+1,count,entity_rows); + CheckWalletError(pwalletTxsMain->GetList(&entity,entStat.m_Generation,start+1,count,entity_rows)); for(int i=0;iGetCount();i++) { @@ -1821,7 +2136,7 @@ Value liststreampublisheritems(const Array& params, bool fHelp) mc_AdjustStartAndCount(&count,&start,pwalletTxsMain->GetListSize(&entity,entStat.m_Generation,NULL)); Array retArray; - pwalletTxsMain->GetList(&entity,entStat.m_Generation,start+1,count,entity_rows); + CheckWalletError(pwalletTxsMain->GetList(&entity,entStat.m_Generation,start+1,count,entity_rows)); for(int i=0;iGetCount();i++) { @@ -1864,7 +2179,7 @@ Value liststreammap_operation(mc_TxEntity *parent_entity,vector& in { mc_AdjustStartAndCount(&count,&start,pwalletTxsMain->GetListSize(parent_entity,NULL)); entity_rows->Clear(); - pwalletTxsMain->GetList(parent_entity,start+1,count,entity_rows); + CheckWalletError(pwalletTxsMain->GetList(parent_entity,start+1,count,entity_rows)); enitity_count=entity_rows->GetCount(); } else @@ -2168,7 +2483,7 @@ int GetAndQueryDirtyList(vector& conditions, mc_EntityDetails { int min_size=max_size+1; int min_condition=conditions_count; - for(i=0;i<=conditions_count;i++) + for(i=0;i& conditions, mc_EntityDetails { throw JSONRPCError(RPC_NOT_SUPPORTED, "This query may take too much time"); } - pwalletTxsMain->GetList(&vConditionEntities[min_condition],entStat.m_Generation,1,min_size,entity_rows); + CheckWalletError(pwalletTxsMain->GetList(&vConditionEntities[min_condition],entStat.m_Generation,1,min_size,entity_rows)); conditions_used++; clean_count=0; dirty_count=0; diff --git a/src/rpc/rpcupgrades.cpp b/src/rpc/rpcupgrades.cpp index 69a317e2..3598d9f1 100644 --- a/src/rpc/rpcupgrades.cpp +++ b/src/rpc/rpcupgrades.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. @@ -70,6 +70,7 @@ Value createupgradefromcmd(const Array& params, bool fHelp) vector addresses; vector fromaddresses; + EnsureWalletIsUnlocked(); if(params[0].get_str() != "*") { @@ -167,12 +168,14 @@ Value createupgradefromcmd(const Array& params, bool fHelp) ( -mc_gState->VersionInfo(protocol_version) != mc_gState->GetNumericVersion() ) ) { strError=strprintf("Invalid value for protocol version. Valid range: %s\n",mc_SupportedProtocols().c_str()); + errorCode=RPC_NOT_ALLOWED; goto exitlbl; } if( protocol_version < mc_gState->MinProtocolDowngradeVersion() ) { strError="Invalid protocol version, cannot downgrade to this version"; + errorCode=RPC_NOT_ALLOWED; goto exitlbl; } if( mc_gState->m_NetworkParams->ProtocolVersion() >= mc_gState->MinProtocolForbiddenDowngradeVersion() ) @@ -210,6 +213,7 @@ Value createupgradefromcmd(const Array& params, bool fHelp) else { strError="Some upgrade parameters are not supported by the current protocol, please upgrade protocol separately first."; + errorCode=RPC_NOT_SUPPORTED; goto exitlbl; } // lpDetails->SetParamValue(s.name_.c_str(),s.name_.size(),(unsigned char*)s.value_.get_str().c_str(),s.value_.get_str().size()); @@ -264,7 +268,6 @@ Value createupgradefromcmd(const Array& params, bool fHelp) scriptOpReturn << vector(elem, elem + elem_size) << OP_DROP << OP_RETURN; - EnsureWalletIsUnlocked(); { LOCK (pwalletMain->cs_wallet_send); @@ -280,6 +283,75 @@ Value createupgradefromcmd(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } +bool ParseStreamFilterApproval(Value param,mc_EntityDetails *stream_entity) +{ + bool field_parsed,for_found,approve_found; + bool approval=false; + + if(param.type() == obj_type) + { + Object objParams = param.get_obj(); + for_found=false; + approve_found=false; + BOOST_FOREACH(const Pair& s, objParams) + { + field_parsed=false; + if(s.name_ == "for") + { + if(for_found) + { + throw JSONRPCError(RPC_INVALID_PARAMETER,"for field can appear only once in the object"); + } + if(s.value_.type() == str_type) + { + ParseEntityIdentifier(s.value_.get_str(),stream_entity, MC_ENT_TYPE_STREAM); + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER,"stream identifier should be string"); + } + field_parsed=true; + for_found=true; + } + if(s.name_ == "approve") + { + if(approve_found) + { + throw JSONRPCError(RPC_INVALID_PARAMETER,"for field can appear only once in the object"); + } + if(s.value_.type() == bool_type) + { + approval=s.value_.get_bool(); + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER,"approve should be boolean"); + } + field_parsed=true; + approve_found=true; + } + if(!field_parsed) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid field: %s",s.name_.c_str())); + } + } + if(!for_found) + { + throw JSONRPCError(RPC_INVALID_PARAMETER,"missing stream identifier"); + } + if(!approve_found) + { + throw JSONRPCError(RPC_INVALID_PARAMETER,"missing approve filed"); + } + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stream approval, should be object"); + } + + return approval; +} + Value approvefrom(const json_spirit::Array& params, bool fHelp) { if (fHelp || params.size() < 3) @@ -293,39 +365,97 @@ Value approvefrom(const json_spirit::Array& params, bool fHelp) string entity_identifier; entity_identifier=params[1].get_str(); - approval=1; - if (params.size() > 2) + timestamp=mc_TimeNowAsUInt(); + + mc_EntityDetails entity; + entity.Zero(); + ParseEntityIdentifier(entity_identifier,&entity, MC_ENT_TYPE_ANY); + + string entity_nameU; + string entity_nameL; + bool fIsUpgrade=true; + bool fIsStreamFilter=false; + + switch(entity.GetEntityType()) { - if(!paramtobool(params[2])) + case MC_ENT_TYPE_UPGRADE: + entity_nameU="Upgrade"; + entity_nameL="upgrade"; + break; + case MC_ENT_TYPE_FILTER: + if(mc_gState->m_Features->Filters() == 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "API is not supported with this protocol version."); + } + if(entity.GetFilterType() != MC_FLT_TYPE_TX) + { + if(mc_gState->m_Features->StreamFilters() == 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "Only Tx filters can be approved/disapproved in this protocol version."); + } + if(entity.GetFilterType() == MC_FLT_TYPE_STREAM) + { + fIsStreamFilter=true; + } + } + entity_nameU="Filter"; + entity_nameL="filter"; + fIsUpgrade=false; + break; + default: + throw JSONRPCError(RPC_ENTITY_NOT_FOUND, "Invalid identifier, should be upgrade or filter"); + break; + } + + mc_EntityDetails stream_entity; + stream_entity.Zero(); + + if(fIsStreamFilter) + { + if (params.size() <= 2) + { + throw JSONRPCError(RPC_INVALID_PARAMS, "Missing stream identifier"); + } + approval=ParseStreamFilterApproval(params[2],&stream_entity); + } + else + { + approval=1; + if (params.size() > 2) { - approval=0; + if(!paramtobool(params[2])) + { + approval=0; + } } } - timestamp=mc_TimeNowAsUInt(); - - mc_EntityDetails entity; - entity.Zero(); - ParseEntityIdentifier(entity_identifier,&entity, MC_ENT_TYPE_UPGRADE); - if( mc_gState->m_NetworkParams->ProtocolVersion() >= mc_gState->MinProtocolForbiddenDowngradeVersion() ) + if(fIsUpgrade) { - if(entity.UpgradeProtocolVersion()) + if( mc_gState->m_NetworkParams->ProtocolVersion() >= mc_gState->MinProtocolForbiddenDowngradeVersion() ) { - if(entity.UpgradeProtocolVersion() < mc_gState->m_NetworkParams->ProtocolVersion()) + if(entity.UpgradeProtocolVersion()) { - throw JSONRPCError(RPC_NOT_ALLOWED, "Invalid protocol version, cannot downgrade from current version"); - } + if(entity.UpgradeProtocolVersion() < mc_gState->m_NetworkParams->ProtocolVersion()) + { + throw JSONRPCError(RPC_NOT_ALLOWED, "Invalid protocol version, cannot downgrade from current version"); + } + } } } - if(mc_gState->m_Permissions->IsApproved(entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET,0)) + if(fIsUpgrade) { - throw JSONRPCError(RPC_NOT_ALLOWED, "Upgrade already approved"); + if(mc_gState->m_Permissions->IsApproved(entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET,0)) + { + throw JSONRPCError(RPC_NOT_ALLOWED, "Upgrade already approved"); + } } - + vector fromaddresses; fromaddresses=ParseAddresses(params[0].get_str(),false,false); + EnsureWalletIsUnlocked(); if(fromaddresses.size() != 1) { @@ -335,43 +465,75 @@ Value approvefrom(const json_spirit::Array& params, bool fHelp) { throw JSONRPCError(RPC_WALLET_ADDRESS_NOT_FOUND, "Private key for from-address is not found in this wallet"); } - + CKeyID *lpKeyID=boost::get (&fromaddresses[0]); - if(lpKeyID != NULL) + if(fIsStreamFilter) { - if(mc_gState->m_Permissions->CanAdmin(NULL,(unsigned char*)(lpKeyID)) == 0) + if(lpKeyID != NULL) { - throw JSONRPCError(RPC_INSUFFICIENT_PERMISSIONS, "from-address doesn't have admin permission"); - } + if(mc_gState->m_Permissions->CanAdmin(stream_entity.GetTxID(),(unsigned char*)(lpKeyID)) == 0) + { + throw JSONRPCError(RPC_INSUFFICIENT_PERMISSIONS, "from-address doesn't have admin permission for specified stream"); + } + } + else + { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Please use raw transactions to approve filters from P2SH addresses"); + } } else { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Please use raw transactions to approve upgrades from P2SH addresses"); + if(lpKeyID != NULL) + { + if(mc_gState->m_Permissions->CanAdmin(NULL,(unsigned char*)(lpKeyID)) == 0) + { + throw JSONRPCError(RPC_INSUFFICIENT_PERMISSIONS, "from-address doesn't have admin permission"); + } + } + else + { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Please use raw transactions to approve upgrades from P2SH addresses"); + } } mc_Script *lpScript=mc_gState->m_TmpBuffers->m_RpcScript3; lpScript->Clear(); - lpScript->SetEntity(entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET); - lpScript->SetApproval(approval, timestamp); - - size_t elem_size; - const unsigned char *elem; CScript scriptOpReturn=CScript(); - for(int e=0;eGetNumElements();e++) + if(fIsUpgrade) + { + lpScript->SetEntity(entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET); + lpScript->SetApproval(approval, timestamp); + size_t elem_size; + const unsigned char *elem; + + for(int e=0;eGetNumElements();e++) + { + elem = lpScript->GetData(e,&elem_size); + scriptOpReturn << vector(elem, elem + elem_size) << OP_DROP; + } + scriptOpReturn << OP_RETURN; + } + else { - elem = lpScript->GetData(e,&elem_size); - scriptOpReturn << vector(elem, elem + elem_size) << OP_DROP; - } - scriptOpReturn << OP_RETURN; + if(fIsStreamFilter) + { + lpScript->SetEntity(stream_entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET); + } + lpScript->SetPermission(MC_PTP_FILTER,0,approval ? 4294967295U : 0,timestamp); + uint160 filter_address; + filter_address=0; + memcpy(&filter_address,entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET,MC_AST_SHORT_TXID_SIZE); + addresses.push_back(CKeyID(filter_address)); + } + - LogPrintf("mchn: %s upgrade %s (%s) from address %s\n",(approval != 0) ? "Approving" : "Disapproving", + LogPrintf("mchn: %s %s %s (%s) from address %s\n",(approval != 0) ? "Approving" : "Disapproving",entity_nameL.c_str(), ((uint256*)entity.GetTxID())->ToString().c_str(),entity.GetName(),CBitcoinAddress(fromaddresses[0]).ToString().c_str()); CWalletTx wtx; - EnsureWalletIsUnlocked(); LOCK (pwalletMain->cs_wallet_send); SendMoneyToSeveralAddresses(addresses, 0, wtx, lpScript, scriptOpReturn, fromaddresses); diff --git a/src/rpc/rpcutils.cpp b/src/rpc/rpcutils.cpp index a3b2e5a8..f585308b 100644 --- a/src/rpc/rpcutils.cpp +++ b/src/rpc/rpcutils.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "rpc/rpcutils.h" @@ -40,8 +40,14 @@ int c_UTF8_charlen[256]={ 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0 }; +bool ParseStreamFilterApproval(Value param,mc_EntityDetails *stream_entity); + CScript RemoveOpDropsIfNeeded(const CScript& scriptInput) { + if(pMultiChainFilterEngine->m_TxID != 0) + { + return scriptInput; + } if (!GetBoolArg("-hideknownopdrops", false)) { return scriptInput; @@ -82,6 +88,21 @@ bool AssetRefDecode(unsigned char *bin, const char* string, const size_t stringL return true; } +int mc_MaxOpReturnShown() +{ + int res=GetArg("-maxshowndata",MAX_OP_RETURN_SHOWN); + if(pMultiChainFilterEngine->m_TxID != 0) + { + res=MAX_OP_RETURN_SHOWN; + if(pMultiChainFilterEngine->m_Params.m_MaxShownData >= 0) + { + res=pMultiChainFilterEngine->m_Params.m_MaxShownData; + } + } + + return res; +} + uint256 mc_GenesisCoinbaseTxID() { if(hGenesisCoinbaseTxID == 0) @@ -143,6 +164,26 @@ Value mc_ExtractDetailsJSONObject(const unsigned char *script,uint32_t total) return value; } +void CheckWalletError(int err) +{ + if(err) + { + switch(err) + { + case MC_ERR_NOT_SUPPORTED: + throw JSONRPCError(RPC_NOT_SUPPORTED, "This feature is not supported in this build"); + break; + case MC_ERR_NOT_ALLOWED: + throw JSONRPCError(RPC_NOT_SUPPORTED, "The index required for this API is not built for this subscription."); + break; + case MC_ERR_INTERNAL_ERROR: + throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal wallet error"); + break; + default: + break; + } + } +} int ParseAssetKey(const char* asset_key,unsigned char *txid,unsigned char *asset_ref,char *name,int *multiple,int *type,int entity_type) { @@ -253,10 +294,17 @@ int ParseAssetKey(const char* asset_key,unsigned char *txid,unsigned char *asset if(entity.IsUnconfirmedGenesis()) { ret=MC_ASSET_KEY_UNCONFIRMED_GENESIS; + if(asset_ref) + { + memset(asset_ref,0,MC_AST_ASSET_REF_SIZE); + } } - if(asset_ref) + else { - memcpy(asset_ref,ptr,MC_AST_ASSET_REF_SIZE); + if(asset_ref) + { + memcpy(asset_ref,ptr,MC_AST_ASSET_REF_SIZE); + } } if(name) { @@ -515,7 +563,6 @@ Array PermissionEntries(const CTxOut& txout,mc_Script *lpScript,bool fLong) { Object entry; entry.push_back(Pair("for", PermissionForFieldEntry(&entity))); -// full_type=mc_gState->m_Permissions->GetPossiblePermissionTypes(entity.GetEntityType()); full_type=mc_gState->m_Permissions->GetPossiblePermissionTypes(&entity); if(full_type & MC_PTP_CONNECT)entry.push_back(Pair("connect", (type & MC_PTP_CONNECT) ? true : false)); if(full_type & MC_PTP_SEND)entry.push_back(Pair("send", (type & MC_PTP_SEND) ? true : false)); @@ -525,7 +572,18 @@ Array PermissionEntries(const CTxOut& txout,mc_Script *lpScript,bool fLong) if(full_type & MC_PTP_ISSUE)entry.push_back(Pair("issue", (type & MC_PTP_ISSUE) ? true : false)); if(full_type & MC_PTP_MINE)entry.push_back(Pair("mine", (type & MC_PTP_MINE) ? true : false)); if(full_type & MC_PTP_ADMIN)entry.push_back(Pair("admin", (type & MC_PTP_ADMIN) ? true : false)); - if(full_type & MC_PTP_ACTIVATE)entry.push_back(Pair("activate", (type & MC_PTP_ACTIVATE) ? true : false)); + if(full_type & MC_PTP_ACTIVATE)entry.push_back(Pair("activate", (type & MC_PTP_ACTIVATE) ? true : false)); + if(mc_gState->m_Features->CustomPermissions()) + { + Array custom_permissions; + if(type & MC_PTP_CUSTOM1)custom_permissions.push_back(MC_PTN_CUSTOM1); + if(type & MC_PTP_CUSTOM2)custom_permissions.push_back(MC_PTN_CUSTOM2); + if(type & MC_PTP_CUSTOM3)custom_permissions.push_back(MC_PTN_CUSTOM3); + if(type & MC_PTP_CUSTOM4)custom_permissions.push_back(MC_PTN_CUSTOM4); + if(type & MC_PTP_CUSTOM5)custom_permissions.push_back(MC_PTN_CUSTOM5); + if(type & MC_PTP_CUSTOM6)custom_permissions.push_back(MC_PTN_CUSTOM6); + entry.push_back(Pair("custom", custom_permissions)); + } entry.push_back(Pair("startblock",(int64_t)from)); entry.push_back(Pair("endblock",(int64_t)to)); entry.push_back(Pair("timestamp",(int64_t)timestamp)); @@ -534,7 +592,10 @@ Array PermissionEntries(const CTxOut& txout,mc_Script *lpScript,bool fLong) txnouttype typeRet; entry.push_back(Pair("addresses", AddressEntries(txout,typeRet))); } - results.push_back(entry); + if( (type & MC_PTP_FILTER) == 0) + { + results.push_back(entry); + } } else { @@ -549,40 +610,35 @@ Array PermissionEntries(const CTxOut& txout,mc_Script *lpScript,bool fLong) return results; } - - -Object StreamEntry(const unsigned char *txid,uint32_t output_level) +Object FilterEntry(const unsigned char *txid,uint32_t output_level,uint32_t filter_type) { // output_level constants // 0x0001 type // 0x0002 txid -// 0x0004 open/details -// 0x0008 subscribed/synchronized -// 0x0010 stats +// 0x0004 details // 0x0020 creators Object entry; mc_EntityDetails entity; - mc_TxEntity tmp_entity; - mc_TxEntityStat entStat; unsigned char *ptr; + size_t value_size; if(txid == NULL) { - entry.push_back(Pair("streamref", "")); + entry.push_back(Pair("filterref", "")); return entry; } uint256 hash=*(uint256*)txid; if(mc_gState->m_Assets->FindEntityByTxID(&entity,txid)) { - ptr=(unsigned char *)entity.GetName(); - - if(output_level & 0x001) + if(entity.GetFilterType() != filter_type) { - entry.push_back(Pair("type", "stream")); + return entry; } + ptr=(unsigned char *)entity.GetName(); + if(ptr && strlen((char*)ptr)) { entry.push_back(Pair("name", string((char*)ptr))); @@ -596,7 +652,7 @@ Object StreamEntry(const unsigned char *txid,uint32_t output_level) if(entity.IsUnconfirmedGenesis()) { Value null_value; - entry.push_back(Pair("streamref",null_value)); + entry.push_back(Pair("filterref",null_value)); } else { @@ -612,7 +668,200 @@ Object StreamEntry(const unsigned char *txid,uint32_t output_level) { streamref="0-0-0"; } - entry.push_back(Pair("streamref", streamref)); + entry.push_back(Pair("filterref", streamref)); + } + + if(output_level & 0x0004) + { + entry.push_back(Pair("language", "javascript")); + + Array entities; + ptr=(unsigned char *)entity.GetSpecialParam(MC_ENT_SPRM_FILTER_CODE,&value_size); + + if(ptr) + { + entry.push_back(Pair("codelength", static_cast(value_size))); + } + else + { + entry.push_back(Pair("codelength", 0)); + } + + if(filter_type == MC_FLT_TYPE_TX) + { + ptr=(unsigned char *)entity.GetSpecialParam(MC_ENT_SPRM_FILTER_RESTRICTIONS,&value_size); + + if(ptr) + { + if(value_size % MC_AST_SHORT_TXID_SIZE) + { + entry.push_back(Pair("for","error")); + } + else + { + for(int i=0;i<(int)value_size/MC_AST_SHORT_TXID_SIZE;i++) + { + mc_EntityDetails relevant_entity; +// Object asset_entry; + if(mc_gState->m_Assets->FindEntityByShortTxID(&relevant_entity,ptr+i*MC_AST_SHORT_TXID_SIZE)) + { + switch(relevant_entity.GetEntityType()) + { + case MC_ENT_TYPE_ASSET: + entities.push_back(AssetEntry(relevant_entity.GetTxID(),-1,0x0104)); +// asset_entry.push_back(Pair("type", "asset")); +// entities.push_back(asset_entry); + break; + default: + entities.push_back(StreamEntry(relevant_entity.GetTxID(),0x03)); + break; + } + } + } + entry.push_back(Pair("for",entities)); + } + } + else + { + entry.push_back(Pair("for",entities)); + } + } + } + + + + if(output_level & 0x0020) + { + Array openers; + int64_t offset,new_offset; + uint32_t value_offset; + ptr=(unsigned char*)entity.GetScript(); + + offset=0; + while(offset>=0) + { + new_offset=entity.NextParam(offset,&value_offset,&value_size); + if(value_offset > 0) + { + if(ptr[offset] == 0) + { + if(ptr[offset+1] == MC_ENT_SPRM_ISSUER) + { + if(value_size == 24) + { + unsigned char tptr[4]; + memcpy(tptr,ptr+value_offset+sizeof(uint160),4); + if(mc_GetLE(tptr,4) & MC_PFL_IS_SCRIPTHASH) + { + openers.push_back(CBitcoinAddress(*(CScriptID*)(ptr+value_offset)).ToString()); + } + else + { + openers.push_back(CBitcoinAddress(*(CKeyID*)(ptr+value_offset)).ToString()); + } + } + } + } + } + offset=new_offset; + } + + entry.push_back(Pair("creators",openers)); + } + } + else + { + Value null_value; + if(output_level & 0x001) + { + + } + entry.push_back(Pair("name",null_value)); + if(output_level & 0x002) + { + entry.push_back(Pair("createtxid",null_value)); + } + entry.push_back(Pair("filterref", null_value)); + } + + return entry; +} + +Object StreamEntry(const unsigned char *txid,uint32_t output_level,mc_EntityDetails *raw_entity) +{ +// output_level constants +// 0x0001 type +// 0x0002 txid +// 0x0004 open/details +// 0x0008 subscribed/synchronized +// 0x0010 stats +// 0x0020 creators +// 0x0040 filters +// 0x0800 skip name and ref + + Object entry; + mc_EntityDetails entity; + mc_TxEntity tmp_entity; + mc_TxEntityStat entStat; + unsigned char *ptr; + + if(txid == NULL) + { + entry.push_back(Pair("streamref", "")); + return entry; + } + + + uint256 hash=*(uint256*)txid; + if( (raw_entity != NULL ) || (mc_gState->m_Assets->FindEntityByTxID(&entity,txid) != 0) ) + { + if(raw_entity) + { + memcpy(&entity,raw_entity,sizeof(mc_EntityDetails)); + } + ptr=(unsigned char *)entity.GetName(); + + if(output_level & 0x001) + { + entry.push_back(Pair("type", "stream")); + } + + if( (output_level & 0x0800) == 0 ) + { + if(ptr && strlen((char*)ptr)) + { + entry.push_back(Pair("name", string((char*)ptr))); + } + } + if(output_level & 0x002) + { + entry.push_back(Pair("createtxid", hash.GetHex())); + } + ptr=(unsigned char *)entity.GetRef(); + string streamref=""; + if( (output_level & 0x0800) == 0 ) + { + if(entity.IsUnconfirmedGenesis()) + { + Value null_value; + entry.push_back(Pair("streamref",null_value)); + } + else + { + if((int)mc_GetLE(ptr,4)) + { + streamref += itostr((int)mc_GetLE(ptr,4)); + streamref += "-"; + streamref += itostr((int)mc_GetLE(ptr+4,4)); + streamref += "-"; + streamref += itostr((int)mc_GetLE(ptr+8,2)); + } + else + { + streamref="0-0-0"; + } + entry.push_back(Pair("streamref", streamref)); + } } if(output_level & 0x0004) @@ -709,7 +958,24 @@ Object StreamEntry(const unsigned char *txid,uint32_t output_level) { entry.push_back(Pair("creators",openers)); } - if(output_level & 0x0018) + + if( ( (output_level & 0x0040) != 0) && (pMultiChainFilterEngine->m_TxID == 0) ) + { + Array filters; + for(int i=0;i<(int)pMultiChainFilterEngine->m_Filters.size();i++) + { + if(pMultiChainFilterEngine->m_Filters[i].m_FilterType == MC_FLT_TYPE_STREAM) + { + if(mc_gState->m_Permissions->FilterApproved(entity.GetTxID(),&(pMultiChainFilterEngine->m_Filters[i].m_FilterAddress))) + { + filters.push_back(FilterEntry(pMultiChainFilterEngine->m_Filters[i].m_Details.GetTxID(),0x02,MC_FLT_TYPE_STREAM)); + } + } + } + entry.push_back(Pair("filters",filters)); + } + + if( ( (output_level & 0x0018) != 0) && (pMultiChainFilterEngine->m_TxID == 0) ) { entStat.Zero(); memcpy(&entStat,entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET,MC_AST_SHORT_TXID_SIZE); @@ -717,7 +983,7 @@ Object StreamEntry(const unsigned char *txid,uint32_t output_level) if(pwalletTxsMain->FindEntity(&entStat)) { if(output_level & 0x0008) - { + { entry.push_back(Pair("subscribed",true)); if(entStat.m_Flags & MC_EFL_NOT_IN_SYNC) { @@ -764,6 +1030,11 @@ Object StreamEntry(const unsigned char *txid,uint32_t output_level) return entry; } +Object StreamEntry(const unsigned char *txid,uint32_t output_level) +{ + return StreamEntry(txid,output_level,NULL); +} + map ParamsToUpgrade(mc_EntityDetails *entity,int version) { map result; @@ -875,7 +1146,7 @@ Value OpReturnEntry(const unsigned char *elem,size_t elem_size,uint256 txid, int { string metadata=""; Object metadata_object; - if((int)elem_size <= GetArg("-maxshowndata",MAX_OP_RETURN_SHOWN)) + if((int)elem_size <= mc_MaxOpReturnShown()) { metadata=HexStr(elem,elem+elem_size); return metadata; @@ -919,7 +1190,7 @@ int mc_IsUTF8(const unsigned char *elem,size_t elem_size) return 1; } -const unsigned char *GetChunkDataInRange(int64_t *out_size,unsigned char* hashes,int chunk_count,int64_t start,int64_t count) +const unsigned char *GetChunkDataInRange(int64_t *out_size,unsigned char* hashes,int chunk_count,int64_t start,int64_t count,int fHan) { mc_ChunkDBRow chunk_def; int size,shift,chunk; @@ -947,8 +1218,7 @@ const unsigned char *GetChunkDataInRange(int64_t *out_size,unsigned char* hashes if(size > MAX_CHUNK_SIZE) { return NULL; - } - + } ptr+=shift; if(pwalletTxsMain->m_ChunkDB->GetChunkDef(&chunk_def,ptr,NULL,NULL,-1) == MC_ERR_NOERROR) @@ -969,7 +1239,21 @@ const unsigned char *GetChunkDataInRange(int64_t *out_size,unsigned char* hashes elem=pwalletTxsMain->m_ChunkDB->GetChunk(&chunk_def,0,-1,&elem_size); if(elem) { - mc_gState->m_TmpBuffers->m_RpcChunkScript1->SetData(elem+read_from,read_size); + if(fHan) + { + if(read_size) + { + if(write(fHan,elem+read_from,read_size) != read_size) + { + return NULL; + } + } + } + else + { + mc_gState->m_TmpBuffers->m_RpcChunkScript1->SetData(elem+read_from,read_size); + } + *out_size+=read_size; } } @@ -997,7 +1281,7 @@ uint32_t GetFormattedData(mc_Script *lpScript,const unsigned char **elem,int64_t if(chunk_count > 1) { - if(total_size <= GetArg("-maxshowndata",MAX_OP_RETURN_SHOWN)) + if(total_size <= mc_MaxOpReturnShown()) { use_tmp_buf=true; } @@ -1179,7 +1463,7 @@ Value OpReturnFormatEntry(const unsigned char *elem,int64_t elem_size,uint256 tx error_str=OffChainError(status,&errorCode); } - if( (((int)elem_size <= GetArg("-maxshowndata",MAX_OP_RETURN_SHOWN)) || (txid == 0)) && available && ((status & MC_OST_ERROR_MASK) == 0) && (elem != NULL) ) + if( (((int)elem_size <= mc_MaxOpReturnShown()) || (txid == 0)) && available && ((status & MC_OST_ERROR_MASK) == 0) && (elem != NULL) ) { if(format_text_out) { @@ -1226,8 +1510,12 @@ Value OpReturnFormatEntry(const unsigned char *elem,int64_t elem_size,uint256 tx { *format_text_out="gettxoutdata"; } - metadata_object.push_back(Pair("txid", txid.ToString())); - metadata_object.push_back(Pair("vout", vout)); + + if( (pMultiChainFilterEngine->m_TxID == 0) || (mc_gState->m_Features->StreamFilters() == 0) ) + { + metadata_object.push_back(Pair("txid", txid.ToString())); + metadata_object.push_back(Pair("vout", vout)); + } metadata_object.push_back(Pair("format", OpReturnFormatToText(format))); metadata_object.push_back(Pair("size", elem_size)); /* @@ -1265,6 +1553,8 @@ Value OpReturnFormatEntry(const unsigned char *elem,size_t elem_size,uint256 txi Value DataItemEntry(const CTransaction& tx,int n,set & already_seen,uint32_t stream_output_level) { // 0x0100 No offchain data + // 0x0200 Skip available + // 0x0400 Top-level format/size Object entry; Array publishers; @@ -1353,21 +1643,24 @@ Value DataItemEntry(const CTransaction& tx,int n,set & already_seen,uin Array chunks; if(retrieve_status & MC_OST_CONTROL_NO_DATA) { - if(format_item_value.type() == obj_type) + if((retrieve_status & MC_OST_STORAGE_MASK) == MC_OST_OFF_CHAIN) { - for(int chunk=0;chunkToString())); - chunk_obj.push_back(Pair("size", chunk_size)); - chunks.push_back(chunk_obj); - chunk_hashes+=MC_CDB_CHUNK_HASH_SIZE; - } -// format_item_value.get_obj().push_back(Pair("chunks", chunks)); + for(int chunk=0;chunkToString())); + chunk_obj.push_back(Pair("size", chunk_size)); + chunks.push_back(chunk_obj); + chunk_hashes+=MC_CDB_CHUNK_HASH_SIZE; + } + // format_item_value.get_obj().push_back(Pair("chunks", chunks)); + } } } @@ -1414,16 +1707,36 @@ Value DataItemEntry(const CTransaction& tx,int n,set & already_seen,uin entry.push_back(Pair("offchain", (retrieve_status & MC_OST_STORAGE_MASK) == MC_OST_OFF_CHAIN)); if( ( retrieve_status & MC_OST_CONTROL_NO_DATA ) == 0) { - entry.push_back(Pair("available", AvailableFromStatus(retrieve_status))); - if(retrieve_status & MC_OST_ERROR_MASK) + if( (stream_output_level & 0x0200) == 0) // USed only for getfilterstreamitem, calloed only if available = true and there is no retrieval error + { + entry.push_back(Pair("available", AvailableFromStatus(retrieve_status))); + if(retrieve_status & MC_OST_ERROR_MASK) + { + string error_str; + int errorCode; + error_str=OffChainError(retrieve_status,&errorCode); + entry.push_back(Pair("error", error_str)); + } + } + } + + if(stream_output_level & 0x0400) + { + entry.push_back(Pair("format", OpReturnFormatToText(format))); + entry.push_back(Pair("size", out_size)); + if(format_text_str == "gettxoutdata") + { + entry.push_back(Pair("data", Value::null)); + } + else { - string error_str; - int errorCode; - error_str=OffChainError(retrieve_status,&errorCode); - entry.push_back(Pair("error", error_str)); + entry.push_back(Pair("data", format_item_value)); } + } + else + { + entry.push_back(Pair("data", format_item_value)); } - entry.push_back(Pair("data", format_item_value)); if(retrieve_status & MC_OST_CONTROL_NO_DATA) @@ -1448,12 +1761,18 @@ Object AssetEntry(const unsigned char *txid,int64_t quantity,uint32_t output_lev // 0x0020 issuers // 0x0040 put given quantity into qty field, even negative // 0x0080 put issueqty into qty field +// 0x0100 skip all quantities and add "type":"asset| Object entry; mc_EntityDetails entity; - mc_EntityDetails genesis_entity; + mc_EntityDetails sec_entity; + mc_EntityDetails *genesis_entity; + mc_EntityDetails *followon; mc_TxEntityStat entStat; unsigned char *ptr; + genesis_entity=&sec_entity; + followon=&sec_entity; + if(txid == NULL) { entry.push_back(Pair("assetref", "")); @@ -1466,16 +1785,22 @@ Object AssetEntry(const unsigned char *txid,int64_t quantity,uint32_t output_lev if(mc_gState->m_Assets->FindEntityByTxID(&entity,txid)) { + genesis_entity->Zero(); if(entity.IsFollowOn()) { - mc_gState->m_Assets->FindEntityByFollowOn(&genesis_entity,txid); + mc_gState->m_Assets->FindEntityByFollowOn(genesis_entity,txid); } else { - memcpy(&genesis_entity,&entity,sizeof(mc_EntityDetails)); + memcpy(genesis_entity,&entity,sizeof(mc_EntityDetails)); } - ptr=(unsigned char *)genesis_entity.GetName(); + if(output_level & 0x100) + { + entry.push_back(Pair("type", "asset")); + } + + ptr=(unsigned char *)genesis_entity->GetName(); if(ptr && strlen((char*)ptr)) { entry.push_back(Pair("name", string((char*)ptr))); @@ -1484,9 +1809,9 @@ Object AssetEntry(const unsigned char *txid,int64_t quantity,uint32_t output_lev { entry.push_back(Pair("issuetxid", hash.GetHex())); } - ptr=(unsigned char *)genesis_entity.GetRef(); + ptr=(unsigned char *)genesis_entity->GetRef(); string assetref=""; - if(genesis_entity.IsUnconfirmedGenesis()) + if(genesis_entity->IsUnconfirmedGenesis()) { Value null_value; entry.push_back(Pair("assetref",null_value)); @@ -1501,6 +1826,9 @@ Object AssetEntry(const unsigned char *txid,int64_t quantity,uint32_t output_lev entry.push_back(Pair("assetref", assetref)); } + entStat.Zero(); + memcpy(&entStat,genesis_entity->GetShortRef(),mc_gState->m_NetworkParams->m_AssetRefSize); + size_t value_size; int64_t offset,new_offset; int64_t raw_output; @@ -1511,8 +1839,8 @@ Object AssetEntry(const unsigned char *txid,int64_t quantity,uint32_t output_lev uint32_t permissions; ptr=entity.GetScript(); - multiple=genesis_entity.GetAssetMultiple(); - permissions=genesis_entity.Permissions(); + multiple=genesis_entity->GetAssetMultiple(); + permissions=genesis_entity->Permissions(); units= 1./(double)multiple; if(output_level & 0x0002) { @@ -1583,26 +1911,27 @@ Object AssetEntry(const unsigned char *txid,int64_t quantity,uint32_t output_lev for(int i=followons->GetCount()-1;i>=0;i--) { Object issue; - mc_EntityDetails followon; - if(mc_gState->m_Assets->FindEntityByTxID(&followon,followons->GetRow(i))) +// mc_EntityDetails followon; + followon->Zero(); + if(mc_gState->m_Assets->FindEntityByTxID(followon,followons->GetRow(i))) { - qty=followon.GetQuantity(); + qty=followon->GetQuantity(); total+=qty; if(output_level & 0x0020) { - issue.push_back(Pair("txid", ((uint256*)(followon.GetTxID()))->ToString().c_str())); + issue.push_back(Pair("txid", ((uint256*)(followon->GetTxID()))->ToString().c_str())); issue.push_back(Pair("qty", (double)qty*units)); issue.push_back(Pair("raw", qty)); Object followon_fields; Array followon_issuers; - vfields=mc_ExtractDetailsJSONObject(&followon); - ptr=followon.GetScript(); + vfields=mc_ExtractDetailsJSONObject(followon); + ptr=followon->GetScript(); offset=0; while(offset>=0) { - new_offset=followon.NextParam(offset,&value_offset,&value_size); + new_offset=followon->NextParam(offset,&value_offset,&value_size); if(value_offset > 0) { if(ptr[offset]) @@ -1659,35 +1988,36 @@ Object AssetEntry(const unsigned char *txid,int64_t quantity,uint32_t output_lev } raw_output=quantity; - if(output_level & 0x0080) + if( (output_level & 0x0100) == 0) { - raw_output=entity.GetQuantity(); - entry.push_back(Pair("qty", (double)raw_output*units)); - entry.push_back(Pair("raw", raw_output)); - } - else - { - if( (raw_output < 0) && ( (output_level & 0x0040) == 0) ) + if(output_level & 0x0080) { - raw_output=total; - entry.push_back(Pair("issueqty", (double)raw_output*units)); - entry.push_back(Pair("issueraw", raw_output)); + raw_output=entity.GetQuantity(); + entry.push_back(Pair("qty", (double)raw_output*units)); + entry.push_back(Pair("raw", raw_output)); } else { - entry.push_back(Pair("qty", (double)raw_output*units)); - if(output_level & 0x0001) + if( (raw_output < 0) && ( (output_level & 0x0040) == 0) ) + { + raw_output=total; + entry.push_back(Pair("issueqty", (double)raw_output*units)); + entry.push_back(Pair("issueraw", raw_output)); + } + else { - entry.push_back(Pair("raw", raw_output)); + entry.push_back(Pair("qty", (double)raw_output*units)); + if(output_level & 0x0001) + { + entry.push_back(Pair("raw", raw_output)); + } } } } - if( ((output_level & 0x0008) != 0) && ((mc_gState->m_WalletMode & MC_WMD_TXS) != 0) ) + if( ((output_level & 0x0008) != 0) && ((mc_gState->m_WalletMode & MC_WMD_TXS) != 0) && (pMultiChainFilterEngine->m_TxID == 0) ) { - entStat.Zero(); - memcpy(&entStat,genesis_entity.GetShortRef(),mc_gState->m_NetworkParams->m_AssetRefSize); entStat.m_Entity.m_EntityType=MC_TET_ASSET | MC_TET_CHAINPOS; if(pwalletTxsMain->FindEntity(&entStat)) { @@ -2052,7 +2382,6 @@ string ParseRawOutputObject(Value param,CAmount& nAmount,mc_Script *lpScript, in if(type_string.size()) { type=mc_gState->m_Permissions->GetPermissionType(type_string.c_str(),&entity); -// type=mc_gState->m_Permissions->GetPermissionType(type_string.c_str(),entity.GetEntityType()); if(entity.GetEntityType() == MC_ENT_TYPE_NONE) { if(required) @@ -2092,7 +2421,7 @@ string ParseRawOutputObject(Value param,CAmount& nAmount,mc_Script *lpScript, in if(a.name_.size()) { asset_name=a.name_; - asset_error=ParseAssetKeyToFullAssetRef(asset_name.c_str(),buf,&multiple,NULL, MC_ENT_TYPE_ASSET); + asset_error=ParseAssetKeyToFullAssetRef(asset_name.c_str(),buf,&multiple,NULL, (verify_level & 0x0200) ? MC_ENT_TYPE_ASSET : MC_ENT_TYPE_ANY); if(asset_error) { goto exitlbl; @@ -2320,7 +2649,7 @@ bool GetTxInputsAsTxOuts(const CTransaction& tx, vector & inputs, vector return result; } -CScript GetScriptForString(string source) +CScript GetScriptForString(string source,uint32_t entity_type,mc_EntityDetails *entity) { vector destinations; @@ -2346,6 +2675,24 @@ CScript GetScriptForString(string source) } else { + entity->Zero(); + if(entity_type != MC_ENT_TYPE_NONE) + { + try + { + ParseEntityIdentifier(destinations[0],entity, entity_type); + uint160 filter_address; + filter_address=0; + memcpy(&filter_address,entity->GetTxID()+MC_AST_SHORT_TXID_OFFSET,MC_AST_SHORT_TXID_SIZE); + return GetScriptForDestination(CKeyID(filter_address)); + } + catch (Object& objError) + { + } + catch (std::exception& e) + { + } + } if (IsHex(destinations[0])) { CPubKey vchPubKey(ParseHex(destinations[0])); @@ -2398,35 +2745,50 @@ vector > ParseRawOutputMultiObject(Object sendTo,int *re setAddress.insert(address); } } - CScript scriptPubKey = GetScriptForString(s.name_); - CAmount nAmount; - if (s.value_.type() != obj_type) - { - nAmount = AmountFromValue(s.value_); - } - else + mc_EntityDetails entity; + + entity.Zero(); + CScript scriptPubKey = GetScriptForString(s.name_,MC_ENT_TYPE_FILTER,&entity); + CAmount nAmount=0; + + if(entity.GetEntityType()) { - mc_Script *lpScript=mc_gState->m_TmpBuffers->m_RpcScript4; - lpScript->Clear(); -// uint256 offer_hash; + uint32_t approval,timestamp; size_t elem_size; const unsigned char *elem; - - nAmount=0; - int eErrorCode; - - string strError=ParseRawOutputObject(s.value_,nAmount,lpScript, required,&eErrorCode); - if(strError.size()) + + approval=0; + timestamp=mc_TimeNowAsUInt(); + mc_Script *lpScript=mc_gState->m_TmpBuffers->m_RpcScript4; + lpScript->Clear(); + if(entity.GetFilterType() == MC_FLT_TYPE_STREAM) { - throw JSONRPCError(eErrorCode, strError); + mc_EntityDetails stream_entity; + stream_entity.Zero(); + if(mc_gState->m_Features->StreamFilters() == 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "Only Tx filters can be approved/disapproved in this protocol version."); + } +// approval=ParseStreamFilterApproval(s.value_.get_obj()[0].value_,&stream_entity); + approval=ParseStreamFilterApproval(s.value_,&stream_entity); + lpScript->SetEntity(stream_entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET); } - -/* - if(lpScript->GetNumElements() > MCP_STD_OP_DROP_COUNT ) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid number of elements in script"); -*/ - + else + { + if ( (s.value_.type() != obj_type) || + (s.value_.get_obj().size() != 1) || + (s.value_.get_obj()[0].name_ != "approve") ) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Filter approval should be object with single field - approve"); + } + approval=1; + if(!paramtobool(s.value_.get_obj()[0].value_)) + { + approval=0; + } + } + lpScript->SetPermission(MC_PTP_FILTER,0,approval ? 4294967295U : 0,timestamp); for(int element=0;element < lpScript->GetNumElements();element++) { elem = lpScript->GetData(element,&elem_size); @@ -2437,6 +2799,46 @@ vector > ParseRawOutputMultiObject(Object sendTo,int *re else throw JSONRPCError(RPC_INTERNAL_ERROR, "Invalid script"); } + } + else + { + if (s.value_.type() != obj_type) + { + nAmount = AmountFromValue(s.value_); + } + else + { + mc_Script *lpScript=mc_gState->m_TmpBuffers->m_RpcScript4; + lpScript->Clear(); + // uint256 offer_hash; + size_t elem_size; + const unsigned char *elem; + + nAmount=0; + int eErrorCode; + + string strError=ParseRawOutputObject(s.value_,nAmount,lpScript, required,&eErrorCode); + if(strError.size()) + { + throw JSONRPCError(eErrorCode, strError); + } + + /* + if(lpScript->GetNumElements() > MCP_STD_OP_DROP_COUNT ) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid number of elements in script"); + */ + + for(int element=0;element < lpScript->GetNumElements();element++) + { + elem = lpScript->GetData(element,&elem_size); + if(elem) + { + scriptPubKey << vector(elem, elem + elem_size) << OP_DROP; + } + else + throw JSONRPCError(RPC_INTERNAL_ERROR, "Invalid script"); + } + } } vecSend.push_back(make_pair(scriptPubKey, nAmount)); @@ -3441,6 +3843,10 @@ void ParseEntityIdentifier(Value entity_identifier,mc_EntityDetails *entity,uint entity_nameU="Upgrade"; entity_nameL="upgrade"; break; + case MC_ENT_TYPE_FILTER: + entity_nameU="Filter"; + entity_nameL="filter"; + break; default: entity_nameU="Entity"; entity_nameL="entity"; @@ -3532,6 +3938,10 @@ bool AssetCompareByRef(Value a,Value b) { assetref_a=p.value_; } + if(p.name_ == "filterref") + { + assetref_a=p.value_; + } } BOOST_FOREACH(const Pair& p, b.get_obj()) @@ -3544,6 +3954,10 @@ bool AssetCompareByRef(Value a,Value b) { assetref_b=p.value_; } + if(p.name_ == "filterref") + { + assetref_b=p.value_; + } } if(assetref_b.type() != str_type) diff --git a/src/rpc/rpcutils.h b/src/rpc/rpcutils.h index f80c34a1..db013c35 100644 --- a/src/rpc/rpcutils.h +++ b/src/rpc/rpcutils.h @@ -1,6 +1,6 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef RPCMULTICHAINUTILS_H @@ -20,6 +20,7 @@ #include "multichain/multichain.h" #include "utils/utilparse.h" #include "wallet/chunkdb.h" +#include "filters/multichainfilter.h" using namespace std; using namespace json_spirit; @@ -42,6 +43,7 @@ using namespace json_spirit; #define MC_DATA_API_PARAM_TYPE_RESERVED1 0x00000010 #define MC_DATA_API_PARAM_TYPE_APPROVAL 0x00000020 #define MC_DATA_API_PARAM_TYPE_CREATE_UPGRADE 0x00000040 +#define MC_DATA_API_PARAM_TYPE_CREATE_FILTER 0x00000080 #define MC_DATA_API_PARAM_TYPE_EMPTY_RAW 0x00000100 #define MC_DATA_API_PARAM_TYPE_RAW 0x00000200 #define MC_DATA_API_PARAM_TYPE_FORMATTED 0x00000400 @@ -108,19 +110,21 @@ Value PermissionForFieldEntry(mc_EntityDetails *lpEntity); Array PerOutputDataEntries(const CTxOut& txout,mc_Script *lpScript,uint256 txid,int vout); Array PermissionEntries(const CTxOut& txout,mc_Script *lpScript,bool fLong); Object StreamEntry(const unsigned char *txid,uint32_t output_level); +Object StreamEntry(const unsigned char *txid,uint32_t output_level,mc_EntityDetails *raw_entity); Object UpgradeEntry(const unsigned char *txid); -const unsigned char *GetChunkDataInRange(int64_t *out_size,unsigned char* hashes,int chunk_count,int64_t start,int64_t count); +const unsigned char *GetChunkDataInRange(int64_t *out_size,unsigned char* hashes,int chunk_count,int64_t start,int64_t count,int fHan); uint32_t GetFormattedData(mc_Script *lpScript,const unsigned char **elem,int64_t *out_size,unsigned char* hashes,int chunk_count,int64_t total_size); Value OpReturnEntry(const unsigned char *elem,size_t elem_size,uint256 txid, int vout); Value OpReturnFormatEntry(const unsigned char *elem,int64_t elem_size,uint256 txid, int vout, uint32_t format, string *format_text_out,uint32_t status); Value OpReturnFormatEntry(const unsigned char *elem,size_t elem_size,uint256 txid, int vout, uint32_t format, string *format_text_out); Value OpReturnFormatEntry(const unsigned char *elem,size_t elem_size,uint256 txid, int vout, uint32_t format); Value DataItemEntry(const CTransaction& tx,int n,set & already_seen,uint32_t stream_output_level); +Object FilterEntry(const unsigned char *txid,uint32_t output_level,uint32_t filter_type); Object AssetEntry(const unsigned char *txid,int64_t quantity,uint32_t output_level); string ParseRawOutputObject(Value param,CAmount& nAmount,mc_Script *lpScript,int *eErrorCode); bool FindPreparedTxOut(CTxOut& txout,COutPoint outpoint,string& reason); bool GetTxInputsAsTxOuts(const CTransaction& tx, vector & inputs, vector & errors,string& reason); -CScript GetScriptForString(string source); +CScript GetScriptForString(string source,uint32_t entity_type,mc_EntityDetails *entity); vector > ParseRawOutputMultiObject(Object sendTo,int *required); CScript ParseRawMetadata(Value param,uint32_t allowed_objects,mc_EntityDetails *given_entity,mc_EntityDetails *found_entity); vector ParseStringList(Value param); @@ -145,7 +149,8 @@ int mc_BinaryCacheFile(string id,int mode); void mc_RemoveBinaryCacheFile(string id); bool AvailableFromStatus(uint32_t status); string OffChainError(uint32_t status,int *errorCode); -bool RawDataParseRestrictParameter(const Value& param,uint32_t *restrict,uint32_t *permissions,string *strError); +bool RawDataParseRestrictParameter(const Value& param,uint32_t *restrict,uint32_t *permissions,int *errorCode,string *strError); +void CheckWalletError(int err); #endif /* RPCMULTICHAINUTILS_H */ diff --git a/src/rpc/rpcwallet.cpp b/src/rpc/rpcwallet.cpp index 277829a9..2ea0c44e 100644 --- a/src/rpc/rpcwallet.cpp +++ b/src/rpc/rpcwallet.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "structs/amount.h" @@ -583,7 +583,10 @@ Value storechunk(const Array& params, bool fHelp) } // pwalletTxsMain->m_ChunkDB->FlushSourceChunks(GetArg("-chunkflushmode",MC_CDB_FLUSH_MODE_COMMIT)); - pwalletTxsMain->m_ChunkDB->FlushSourceChunks(GetArg("-flushsourcechunks",true) ? (MC_CDB_FLUSH_MODE_FILE | MC_CDB_FLUSH_MODE_DATASYNC) : MC_CDB_FLUSH_MODE_NONE); + if(pwalletTxsMain->m_ChunkDB->FlushSourceChunks(GetArg("-flushsourcechunks",true) ? (MC_CDB_FLUSH_MODE_FILE | MC_CDB_FLUSH_MODE_DATASYNC) : MC_CDB_FLUSH_MODE_NONE)) + { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't store offchain items, probably chunk database is corrupted"); + } pwalletTxsMain->m_ChunkDB->Dump("storechunk"); return hash.GetHex(); @@ -591,13 +594,8 @@ Value storechunk(const Array& params, bool fHelp) - - -Value gettxoutdata(const Array& params, bool fHelp) +Value txoutdata_operation(const Array& params,int fHan) { - if (fHelp || params.size() < 2 || params.size() > 4) // MCHN - throw runtime_error("Help message not found\n"); - uint256 hash(params[0].get_str()); int n = params[1].get_int(); @@ -645,7 +643,7 @@ Value gettxoutdata(const Array& params, bool fHelp) uint32_t format; unsigned char *chunk_hashes; - int chunk_count; + int chunk_count=0; int64_t total_chunk_size,out_size; uint32_t retrieve_status; size_t elem_size; @@ -717,7 +715,6 @@ Value gettxoutdata(const Array& params, bool fHelp) start=paramtoint64(params[3],false,0,"Invalid start"); } - if(start < 0) { start=out_size+start; @@ -738,6 +735,10 @@ Value gettxoutdata(const Array& params, bool fHelp) if( (format == MC_SCR_DATA_FORMAT_UBJSON) || (format == MC_SCR_DATA_FORMAT_UTF8) ) { + if(fHan) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "This API is not supported for text and JSON data"); + } if(start != 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start, must be 0 for text and JSON data"); @@ -747,16 +748,41 @@ Value gettxoutdata(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid count, must include all text or JSON data"); } } + + if(fHan) + { + if(chunk_count > 1) + { + if(elem == NULL) + { + elem=GetChunkDataInRange(&out_size,chunk_hashes,chunk_count,start,count,fHan); + if(elem == NULL) + { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't retrieve data for this output"); + } + return count; + } + } + else + { + if(write(fHan,elem+start,count) != count) + { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Cannot store binary cache item"); + } + return count; + } + } + if(count > 0x4000000) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid count, must be below 64MB"); } - + if(chunk_count > 1) { if(elem == NULL) { - elem=GetChunkDataInRange(&out_size,chunk_hashes,chunk_count,start,count); + elem=GetChunkDataInRange(&out_size,chunk_hashes,chunk_count,start,count,0); if(elem == NULL) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't retrieve data for this output"); @@ -764,7 +790,55 @@ Value gettxoutdata(const Array& params, bool fHelp) return OpReturnFormatEntry(elem,count,0,0,format,NULL); } } - return OpReturnFormatEntry(elem+start,count,0,0,format,NULL); + return OpReturnFormatEntry(elem+start,count,0,0,format,NULL); +} + +Value txouttobinarycache(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 5) // MCHN + throw runtime_error("Help message not found\n"); + + int64_t size; + + int fHan=mc_BinaryCacheFile(params[0].get_str(),2); + if(fHan <= 0) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Binary cache item with this identifier not found"); + } + + size=lseek64(fHan,0,SEEK_END); + + if(size) + { + close(fHan); + throw JSONRPCError(RPC_INTERNAL_ERROR, "Binary cache item is not empty"); + } + + Array ext_params; + int param_count=0; + BOOST_FOREACH(const Value& value, params) + { + if(param_count) + { + ext_params.push_back(value); + } + param_count++; + } + + size=txoutdata_operation(ext_params,fHan).get_int64(); + + close(fHan); + + return size; + +} + +Value gettxoutdata(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) // MCHN + throw runtime_error("Help message not found\n"); + + return txoutdata_operation(params,0); } /* MCHN END */ @@ -1765,6 +1839,7 @@ Value listsinceblock(const Array& params, bool fHelp) while(up_tx-down_tx>chunk_size) { err=pwalletTxsMain->GetList(&wallet_entity,this_tx,1,lpEntRowBuffer); + CheckWalletError(err); if( (err == MC_ERR_NOERROR) && (lpEntRowBuffer->GetCount() > 0) ) { entrow=(mc_TxEntityRow *)(lpEntRowBuffer->GetRow(0)); @@ -1788,6 +1863,7 @@ Value listsinceblock(const Array& params, bool fHelp) while(this_tx<=tx_count) { err=pwalletTxsMain->GetList(&wallet_entity,this_tx,chunk_size,lpEntRowBuffer); + CheckWalletError(err); if( (err == MC_ERR_NOERROR) && (lpEntRowBuffer->GetCount() > 0) ) { for(int i=0;iGetCount();i++) diff --git a/src/rpc/rpcwallet.h b/src/rpc/rpcwallet.h index b212d645..ec3c801c 100644 --- a/src/rpc/rpcwallet.h +++ b/src/rpc/rpcwallet.h @@ -1,6 +1,6 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef RPCWALLET_H diff --git a/src/rpc/rpcwalletsend.cpp b/src/rpc/rpcwalletsend.cpp index 550a732b..2951f426 100644 --- a/src/rpc/rpcwalletsend.cpp +++ b/src/rpc/rpcwalletsend.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. @@ -22,6 +22,7 @@ Value createrawsendfrom(const Array& params, bool fHelp) set thisFromAddresses; fromaddresses=ParseAddresses(params[0].get_str(),false,true); + EnsureWalletIsUnlocked(); if(fromaddresses.size() != 1) { @@ -110,7 +111,6 @@ Value createrawsendfrom(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Either addresses object or data array should not be empty"); } - EnsureWalletIsUnlocked(); { LOCK (pwalletMain->cs_wallet_send); int eErrorCode; @@ -229,6 +229,7 @@ Value sendfromaddress(const Array& params, bool fHelp) } + EnsureWalletIsUnlocked(); vector fromaddresses; if(params[0].get_str() != "*") @@ -269,8 +270,6 @@ Value sendfromaddress(const Array& params, bool fHelp) } - EnsureWalletIsUnlocked(); - LOCK (pwalletMain->cs_wallet_send); SendMoneyToSeveralAddresses(addresses, nAmount, wtx, lpScript, CScript(), fromaddresses); @@ -333,6 +332,7 @@ Value sendwithmetadatafrom(const Array& params, bool fHelp) vector fromaddresses; set thisFromAddresses; + EnsureWalletIsUnlocked(); if(params[0].get_str() != "*") { @@ -390,7 +390,6 @@ Value sendwithmetadatafrom(const Array& params, bool fHelp) - EnsureWalletIsUnlocked(); LOCK (pwalletMain->cs_wallet_send); SendMoneyToSeveralAddresses(addresses, nAmount, wtx, lpScript, scriptOpReturn, fromaddresses); @@ -585,6 +584,7 @@ Value preparelockunspentfrom(const json_spirit::Array& params, bool fHelp) vector fromaddresses; fromaddresses=ParseAddresses(params[0].get_str(),false,false); + EnsureWalletIsUnlocked(); if(fromaddresses.size() != 1) { @@ -643,15 +643,6 @@ Value preparelockunspentfrom(const json_spirit::Array& params, bool fHelp) CWalletTx wtx; - - - EnsureWalletIsUnlocked(); - LOCK (pwalletMain->cs_wallet_send); - - SendMoneyToSeveralAddresses(addresses, nAmount, wtx, lpScript, CScript(), fromaddresses); - - int vout=-1; - CScript scriptPubKey = GetScriptForDestination(addresses[0]); for(int element=0;element < lpScript->GetNumElements();element++) @@ -667,6 +658,14 @@ Value preparelockunspentfrom(const json_spirit::Array& params, bool fHelp) throw JSONRPCError(RPC_INTERNAL_ERROR, "Invalid script"); } + + + LOCK (pwalletMain->cs_wallet_send); + + SendMoneyToSeveralAddresses(addresses, nAmount, wtx, lpScript, CScript(), fromaddresses); + + int vout=-1; + CScript::const_iterator pc0 = scriptPubKey.begin(); for (unsigned int j = 0; j < wtx.vout.size(); j++) @@ -731,6 +730,7 @@ Value preparelockunspent(const json_spirit::Array& params, bool fHelp) uint256 offer_hash; bool lock_it=true; + EnsureWalletIsUnlocked(); if (params[0].type() != obj_type) { @@ -755,18 +755,6 @@ Value preparelockunspent(const json_spirit::Array& params, bool fHelp) } } - CWalletTx wtx; - - - vector fromaddresses; - - EnsureWalletIsUnlocked(); - LOCK (pwalletMain->cs_wallet_send); - - SendMoneyToSeveralAddresses(addresses, nAmount, wtx, lpScript, CScript(), fromaddresses); - - int vout=-1; - CScript scriptPubKey = GetScriptForDestination(addresses[0]); for(int element=0;element < lpScript->GetNumElements();element++) @@ -782,8 +770,19 @@ Value preparelockunspent(const json_spirit::Array& params, bool fHelp) throw JSONRPCError(RPC_INTERNAL_ERROR, "Invalid script"); } - CScript::const_iterator pc0 = scriptPubKey.begin(); + CWalletTx wtx; + + vector fromaddresses; + + LOCK (pwalletMain->cs_wallet_send); + + SendMoneyToSeveralAddresses(addresses, nAmount, wtx, lpScript, CScript(), fromaddresses); + + int vout=-1; + + + CScript::const_iterator pc0 = scriptPubKey.begin(); for (unsigned int j = 0; j < wtx.vout.size(); j++) { CTxOut txout=wtx.vout[j]; @@ -887,6 +886,7 @@ Value sendassetfrom(const Array& params, bool fHelp) lpScript->SetAssetQuantities(lpBuffer,MC_SCR_ASSET_SCRIPT_TYPE_TRANSFER); + EnsureWalletIsUnlocked(); vector addresses; addresses.push_back(address.Get()); @@ -929,7 +929,6 @@ Value sendassetfrom(const Array& params, bool fHelp) } } - EnsureWalletIsUnlocked(); LOCK (pwalletMain->cs_wallet_send); SendMoneyToSeveralAddresses(addresses, nAmount, wtx, lpScript, CScript(),fromaddresses); diff --git a/src/rpc/rpcwallettxs.cpp b/src/rpc/rpcwallettxs.cpp index c184a775..f0324954 100644 --- a/src/rpc/rpcwallettxs.cpp +++ b/src/rpc/rpcwallettxs.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. @@ -527,7 +527,7 @@ Value listwallettransactions(const Array& params, bool fHelp) { wallet_by_time.m_EntityType |= MC_TET_WALLET_SPENDABLE; } - pwalletTxsMain->GetList(&wallet_by_time,-nFrom,nCount,entity_rows); + CheckWalletError(pwalletTxsMain->GetList(&wallet_by_time,-nFrom,nCount,entity_rows)); for(int i=0;iGetCount();i++) { lpEntTx=(mc_TxEntityRow*)entity_rows->GetRow(i); @@ -658,7 +658,7 @@ Value listaddresstransactions(const Array& params, bool fHelp) memcpy(address_by_time.m_EntityID,lpScriptID,MC_TDB_ENTITY_ID_SIZE); address_by_time.m_EntityType=MC_TET_SCRIPT_ADDRESS | MC_TET_TIMERECEIVED; } - pwalletTxsMain->GetList(&address_by_time,-nFrom,nCount,entity_rows); + CheckWalletError(pwalletTxsMain->GetList(&address_by_time,-nFrom,nCount,entity_rows)); for(int i=0;iGetCount();i++) { lpEntTx=(mc_TxEntityRow*)entity_rows->GetRow(i); diff --git a/src/rpc/rpcwalletutils.cpp b/src/rpc/rpcwalletutils.cpp index 74804c38..9f2b3cf0 100644 --- a/src/rpc/rpcwalletutils.cpp +++ b/src/rpc/rpcwalletutils.cpp @@ -1,11 +1,13 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "rpc/rpcwallet.h" +int VerifyNewTxForStreamFilters(const CTransaction& tx,std::string &strResult,mc_MultiChainFilter **lppFilter,int *applied); +string OpReturnFormatToText(int format); void MinimalWalletTxToJSON(const CWalletTx& wtx, Object& entry) { @@ -217,6 +219,17 @@ void SendMoneyToSeveralAddresses(const std::vector addresses, CA throw JSONRPCError(eErrorCode, strError); } + + mc_MultiChainFilter* lpFilter; + int applied=0; + string filter_error=""; + + if(VerifyNewTxForStreamFilters(wtxNew,filter_error,&lpFilter,&applied) == MC_ERR_NOT_ALLOWED) + { + throw JSONRPCError(RPC_NOT_ALLOWED, "Transaction didn't pass stream filter " + lpFilter->m_FilterCaption + ": " + filter_error); + } + + string strRejectReason; if (!pwalletMain->CommitTransaction(wtxNew, reservekey, strRejectReason)) { @@ -834,18 +847,87 @@ Object StreamItemEntry(const CWalletTx& wtx,int first_output,const unsigned char entry.push_back(Pair("key", keys[0])); } entry.push_back(Pair("offchain", (retrieve_status & MC_OST_STORAGE_MASK) == MC_OST_OFF_CHAIN)); + string full_error=""; if( ( retrieve_status & MC_OST_CONTROL_NO_DATA ) == 0) - { - entry.push_back(Pair("available", AvailableFromStatus(retrieve_status))); + { if(retrieve_status & MC_OST_ERROR_MASK) { - string error_str; int errorCode; - error_str=OffChainError(retrieve_status,&errorCode); - entry.push_back(Pair("error", error_str)); + full_error=OffChainError(retrieve_status,&errorCode); + entry.push_back(Pair("available", false)); + entry.push_back(Pair("error", full_error)); + } + else + { +// if( AvailableFromStatus(retrieve_status) && ((retrieve_status & MC_OST_STORAGE_MASK) == MC_OST_OFF_CHAIN )) + if( AvailableFromStatus(retrieve_status) ) + { + mc_MultiChainFilter* lpFilter; + int applied=0; + string filter_error=""; + mc_TxDefRow txdef; + if(pwalletTxsMain->FindWalletTx(wtx.GetHash(),&txdef)) + { + full_error="Error while retreiving tx from the wallet"; + } + else + { + int filter_block=-1; + int filter_offset=0; + if( (txdef.m_Block >= 0) && (txdef.m_Block <= chainActive.Height())) + { + filter_block=txdef.m_Block; + filter_offset=txdef.m_BlockTxOffset; + } + else + { + filter_offset=-1; + } + + if(pMultiChainFilterEngine->RunStreamFilters(wtx,stream_output,(unsigned char *)stream_id,filter_block,filter_offset, + filter_error,&lpFilter,&applied) != MC_ERR_NOERROR) + { + full_error="Error while running stream filters"; + } + else + { + if(filter_error.size()) + { + full_error=strprintf("Stream item did not pass filter %s: %s",lpFilter->m_FilterCaption.c_str(),filter_error.c_str()); + } + } + } + if(full_error.size()) + { + entry.push_back(Pair("available", false)); + entry.push_back(Pair("error", full_error)); + Object metadata_object; + metadata_object.push_back(Pair("txid", wtx.GetHash().ToString())); + metadata_object.push_back(Pair("vout", stream_output)); + metadata_object.push_back(Pair("format", OpReturnFormatToText(format))); + metadata_object.push_back(Pair("size", out_size)); + format_item_value=metadata_object; + } + else + { + entry.push_back(Pair("available", AvailableFromStatus(retrieve_status))); + } + } + else + { + entry.push_back(Pair("available", AvailableFromStatus(retrieve_status))); + } } } - entry.push_back(Pair("data", format_item_value)); + + if(full_error.size()) + { + entry.push_back(Pair("data", Value::null)); + } + else + { + entry.push_back(Pair("data", format_item_value)); + } if(verbose) { @@ -1009,7 +1091,7 @@ void AppendOffChainFormatData(uint32_t data_format, mc_TxEntity entity; entity.Zero(); entity.m_EntityType=MC_TET_AUTHOR; - + if(out_options & MC_RFD_OPTION_OFFCHAIN) { chunk_count=(int)vValue.size()/MC_CDB_CHUNK_HASH_SIZE; @@ -1018,7 +1100,7 @@ void AppendOffChainFormatData(uint32_t data_format, *strError="Too many chunks in the script"; return; } - + lpDetailsScript->SetChunkDefHeader(data_format,chunk_count); for(int i=0;iSetChunkDefHeader(data_format,0); + } if(fHan > 0) { close(fHan); diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 05bb1734..b0482741 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "bitcoinconsensus.h" diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 40a094a9..dd09ff38 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_BITCOINCONSENSUS_H diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 8f08b69b..77c46a98 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "interpreter.h" @@ -1109,9 +1109,10 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn { if(mc_gState->m_NetworkParams->IsProtocolMultichain()) { - const unsigned char *pubkey_hash=(unsigned char *)Hash160(vchPubKey.begin(),vchPubKey.end()).begin(); - - if(mc_gState->m_Permissions->CanSend(NULL,pubkey_hash)) +// const unsigned char *pubkey_hash=(unsigned char *)Hash160(vchPubKey.begin(),vchPubKey.end()).begin(); +// if(mc_gState->m_Permissions->CanSend(NULL,pubkey_hash)) + uint160 hash=Hash160(vchPubKey.begin(),vchPubKey.end()); + if(mc_gState->m_Permissions->CanSend(NULL,&hash)) { CheckSendPermission=false; } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 0406c234..973a7c68 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/script.cpp b/src/script/script.cpp index 5f6e8947..a24bc6d7 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "script.h" diff --git a/src/script/script.h b/src/script/script.h index 6bc23673..935febdd 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_SCRIPT_SCRIPT_H diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index cf39d40f..1308d2e7 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "script_error.h" diff --git a/src/script/script_error.h b/src/script/script_error.h index 4bdfccca..4ee17c3c 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_SCRIPT_SCRIPT_ERROR_H diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 5bbcfd56..8121a72f 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "sigcache.h" diff --git a/src/script/sigcache.h b/src/script/sigcache.h index dd53157c..49c0658a 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_SCRIPT_SIGCACHE_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 8a2126f5..60d179ad 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "utils/util.h" diff --git a/src/script/sign.h b/src/script/sign.h index 81e71992..7530c391 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_SCRIPT_SIGN_H diff --git a/src/script/standard.cpp b/src/script/standard.cpp index ce040d90..9212482c 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "script/standard.h" diff --git a/src/script/standard.h b/src/script/standard.h index 3982c964..f837e445 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_SCRIPT_STANDARD_H diff --git a/src/storage/addrman.cpp b/src/storage/addrman.cpp index 2afac08c..ff39505d 100644 --- a/src/storage/addrman.cpp +++ b/src/storage/addrman.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2012 Pieter Wuille // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "storage/addrman.h" @@ -59,7 +59,7 @@ bool CAddrInfo::IsTerrible(int64_t nNow) const return false; } -double CAddrInfo::GetChance(int64_t nNow) const +double CAddrInfo::GetChance(int64_t nNow,bool *fDead) const { double fChance = 1.0; @@ -71,6 +71,18 @@ double CAddrInfo::GetChance(int64_t nNow) const if (nSinceLastTry < 0) nSinceLastTry = 0; + if(fDead) + { + *fDead=false; + if(nAttempts) + { + if(nSinceLastSeen > 86400) + { + *fDead=true; + } + } + } + fChance *= 600.0 / (600.0 + nSinceLastSeen); // deprioritize very recent attempts away @@ -405,10 +417,236 @@ void CAddrMan::Attempt_(const CService& addr, int64_t nTime) info.nAttempts++; } -CAddress CAddrMan::Select_(int nUnkBias) +void CAddrMan::SetSC_(bool invalid,int64_t nNow) +{ + if(nSCSelected >= 0) + { + double dOldChance,dNewChance; + bool fOldInvalid,fNewInvalid; + bool fOldDead,fNewDead; + + std::map::iterator it = mapInfo.find(nSCSelected); + if(it == mapInfo.end()) + { + return; + } + + dOldChance=it->second.GetSC(&fOldInvalid,&fOldDead); + it->second.SetSC(invalid,nNow); + dNewChance=it->second.GetSC(&fNewInvalid,&fNewDead); + + if(!fNewInvalid && fOldInvalid) + { + nSCTotalBad--; + } + if(!fOldInvalid && fNewInvalid) + { + nSCTotalBad++; + } + + std::map::iterator scit = mapSCAlive.find(it->second.ToStringIPPort()); + if(scit != mapSCAlive.end()) + { + dSCTotalChance -= dOldChance; + if(!fNewInvalid && !fNewDead) + { + dSCTotalChance += dNewChance; + } + else + { + mapSCAlive.erase(scit); + } + } + else + { + if(!fNewInvalid && !fNewDead) + { + dSCTotalChance += dNewChance; + mapSCAlive.insert(make_pair(it->second.ToStringIPPort(),nSCSelected)); + } + } + } +} + +void CAddrMan::SCRecalculate_(int64_t nNow) +{ + if(mapInfo.size() > 10000) + { + return; + } + + if(nNow - nLastRecalculate < 2 * 60) + { + return; + } + + LogPrint("addrman","Updating address lists for selection\n"); + + nLastRecalculate=nNow; + dSCTotalChance=0; + mapSCAlive.clear(); + + for (std::map::iterator it = mapInfo.begin(); it != mapInfo.end(); ++it) + { + double dNewChance; + bool fNewInvalid; + bool fNewDead; + int64_t nLastTried; + + dNewChance=it->second.GetSC(&fNewInvalid,&fNewDead,&nLastTried); + + if(nLastTried) + { + if(!fNewInvalid && !fNewDead) + { + dSCTotalChance += dNewChance; + mapSCAlive.insert(make_pair(it->second.ToStringIPPort(),it->first)); + } + } + } +} + + +bool CAddrMan::SCSelect_(int nUnkBias,int nNodes,CAddress &addr) +{ + nSCSelected=-1; + if(mapSCAlive.size() > 200) + { + return false; + } + if(mapInfo.size() > 10000) + { + return false; + } + + bool fChooseAlive=true; + + int nAlive=(int)mapSCAlive.size(); + int nUnknown=(int)mapInfo.size()-nAlive-nSCTotalBad; + int64_t nNow = GetAdjustedTime(); + int64_t m = 1<<30; + + + if(nAlive <= nNodes) + { + fChooseAlive=false; + if(nUnknown <= 0) + { + return false; + } + } + else + { + if(nUnknown>0) + { + double nCorAlive = sqrt(nAlive) * (100.0 - nUnkBias); + double nCorUnknown = sqrt(nUnknown) * nUnkBias; // prefer Unknown addresses if we are well connected + double prob = (double)GetRandInt(m) / (double)(m); + if ((nCorAlive + nCorUnknown) *prob < nCorUnknown) + { + fChooseAlive=false; + } + } + } + + if(fChooseAlive) + { + double prob = (double)GetRandInt(m)* dSCTotalChance / (double)(m); + double sum=0; + for (std::map::const_iterator scit = mapSCAlive.begin(); scit != mapSCAlive.end(); ++scit) + { + std::map::iterator it = mapInfo.find(scit->second); + if(it != mapInfo.end()) + { + double dNewChance; + bool fNewInvalid; + bool fNewDead; + + dNewChance=it->second.GetSC(&fNewInvalid,&fNewDead); + sum+=dNewChance; + if(sum >= prob) + { + nSCSelected=scit->second; + addr=it->second; + LogPrint("addrman","Selected recent address %s, last seen: %ds\n",addr.ToStringIPPort().c_str(),nNow - addr.nTime); + return true; + } + } + } + } + else + { + int nTriedDead=-1; + int64_t LastTryDead=nNow; + + for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ++it) + { + bool fNewInvalid; + bool fNewDead; + int64_t nLastTried; + it->second.GetSC(&fNewInvalid,&fNewDead,&nLastTried); + + if(!fNewInvalid) + { + if(nLastTried == 0) + { + nSCSelected=it->first; + addr=it->second; + LogPrint("addrman","Selected first-attempt address %s\n",addr.ToStringIPPort().c_str()); + return true; + } + + if(fNewDead) + { + if(nLastTried <= LastTryDead) + { + LastTryDead=nLastTried; + nTriedDead=it->first; + } + } + } + } + + if(nTriedDead >= 0) + { + MilliSleep(100); // All is dead, no need to hurry + + if(GetRandInt(m) < (0.09 + 0.01 * nUnkBias) * m) // If we are well connected, don't try to connect to confirmed deads too often + { + addr=CAddress(); + return true; + } + + std::map::iterator it = mapInfo.find(nTriedDead); + if(it != mapInfo.end()) + { + nSCSelected=nTriedDead; + addr=it->second; + LogPrint("addrman","Selected old address %s, last seen: %.1fhrs\n",addr.ToStringIPPort().c_str(), + (double)(nNow - addr.nTime)/3600.0); + return true; + } + + return false; + } + } + + return false; +} + + +CAddress CAddrMan::Select_(int nUnkBias,int nNodes) { if (size() == 0) return CAddress(); + + LogPrint("addrman","Selecting address, total: %d, live: %d, invalid: %d, connected: %d\n",(int)mapInfo.size(),(int)mapSCAlive.size(),nSCTotalBad,nNodes); + + CAddress addr; + if(SCSelect_(nUnkBias,nNodes,addr)) + { + return addr; + } double nCorTried = sqrt(nTried) * (100.0 - nUnkBias); double nCorNew = sqrt(nNew) * nUnkBias; @@ -424,7 +662,10 @@ CAddress CAddrMan::Select_(int nUnkBias) assert(mapInfo.count(vTried[nPos]) == 1); CAddrInfo& info = mapInfo[vTried[nPos]]; if (GetRandInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30)) + { + LogPrint("addrman","Selected tried address %s\n",info.ToStringIPPort().c_str()); return info; + } fChanceFactor *= 1.2; } } else { @@ -442,7 +683,10 @@ CAddress CAddrMan::Select_(int nUnkBias) assert(mapInfo.count(*it) == 1); CAddrInfo& info = mapInfo[*it]; if (GetRandInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30)) + { + LogPrint("addrman","Selected new address %s\n",info.ToStringIPPort().c_str()); return info; + } fChanceFactor *= 1.2; } } diff --git a/src/storage/addrman.h b/src/storage/addrman.h index 54cf3715..da0dba2b 100644 --- a/src/storage/addrman.h +++ b/src/storage/addrman.h @@ -1,6 +1,6 @@ // Copyright (c) 2012 Pieter Wuille // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_ADDRMAN_H @@ -46,6 +46,10 @@ class CAddrInfo : public CAddress int nRandomPos; friend class CAddrMan; + + bool fSCInvalid; + bool fSCDead; + double dSCChance; public: @@ -67,6 +71,9 @@ class CAddrInfo : public CAddress nRefCount = 0; fInTried = false; nRandomPos = -1; + fSCInvalid=false; + fSCDead=false; + dSCChance=false; } CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource) @@ -79,6 +86,23 @@ class CAddrInfo : public CAddress Init(); } + void SetSC(bool invalid,int64_t nNow) + { + fSCInvalid=invalid; + dSCChance=GetChance(nNow,&fSCDead); + } + + double GetSC(bool *invalid,bool *dead,int64_t *last_attempt = NULL) const + { + *invalid=fSCInvalid; + *dead=fSCDead; + if(last_attempt) + { + *last_attempt=nLastTry; + } + return dSCChance; + } + //! Calculate in which "tried" bucket this entry belongs int GetTriedBucket(const std::vector &nKey) const; @@ -95,7 +119,7 @@ class CAddrInfo : public CAddress bool IsTerrible(int64_t nNow = GetAdjustedTime()) const; //! Calculate the relative chance this entry should be given when selecting nodes to connect to - double GetChance(int64_t nNow = GetAdjustedTime()) const; + double GetChance(int64_t nNow = GetAdjustedTime(),bool *fDead = NULL) const; }; @@ -203,6 +227,12 @@ class CAddrMan //! list of "new" buckets std::vector > vvNew; + + std::map mapSCAlive; + int nSCSelected; + double dSCTotalChance; + int nSCTotalBad; + int64_t nLastRecalculate; protected: @@ -241,8 +271,14 @@ class CAddrMan //! Select an address to connect to. //! nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100) - CAddress Select_(int nUnkBias); - + CAddress Select_(int nUnkBias,int nNodes); + + bool SCSelect_(int nUnkBias,int nNodes, CAddress &addr); + void SCRecalculate_(int64_t nNow); + + void SetSC_(bool invalid,int64_t nNow); + + #ifdef DEBUG_ADDRMAN //! Perform consistency check. Returns an error code or zero. int Check_(); @@ -406,6 +442,11 @@ class CAddrMan nIdCount = 0; nTried = 0; nNew = 0; + + nSCSelected=-1; + dSCTotalChance=0.; + nSCTotalBad=0; + nLastRecalculate=0; } //! Return the number of (unique) addresses in all tables. @@ -484,17 +525,37 @@ class CAddrMan * Choose an address to connect to. * nUnkBias determines how much "new" entries are favored over "tried" ones (0-100). */ - CAddress Select(int nUnkBias = 50) + CAddress Select(int nUnkBias = 50,int nNodes = 0) { CAddress addrRet; { LOCK(cs); Check(); - addrRet = Select_(nUnkBias); + addrRet = Select_(nUnkBias,nNodes); Check(); } return addrRet; } + + void SetSC(bool invalid,int64_t nNow) + { + { + LOCK(cs); + Check(); + SetSC_(invalid,nNow); + Check(); + } + } + + void SCRecalculate(int64_t nNow) + { + { + LOCK(cs); + Check(); + SCRecalculate_(nNow); + Check(); + } + } //! Return a bunch of addresses, selected at random. std::vector GetAddr() @@ -519,6 +580,20 @@ class CAddrMan Check(); } } + + void Print() + { + { + LOCK(cs); + + int64_t nNow = GetAdjustedTime(); + LogPrint("addrman", "Printing %u addresses\n", mapInfo.size()); + for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ++it) + { + LogPrint("addrman", "%d: %s, Last Seen: %lds, Last Tried: %lds\n", it->first,it->second.ToStringIPPort().c_str(),nNow-it->second.nTime,nNow-it->second.nLastTry); + } + } + } }; #endif // BITCOIN_ADDRMAN_H diff --git a/src/storage/coins.cpp b/src/storage/coins.cpp index f73c92ef..54d251ca 100644 --- a/src/storage/coins.cpp +++ b/src/storage/coins.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2012-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "storage/coins.h" diff --git a/src/storage/coins.h b/src/storage/coins.h index 878fda9e..05138496 100644 --- a/src/storage/coins.h +++ b/src/storage/coins.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_COINS_H diff --git a/src/storage/leveldbwrapper.cpp b/src/storage/leveldbwrapper.cpp index 8c5a9236..24efd8a0 100644 --- a/src/storage/leveldbwrapper.cpp +++ b/src/storage/leveldbwrapper.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2012-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "storage/leveldbwrapper.h" diff --git a/src/storage/leveldbwrapper.h b/src/storage/leveldbwrapper.h index 7cd7a58d..52e14a3a 100644 --- a/src/storage/leveldbwrapper.h +++ b/src/storage/leveldbwrapper.h @@ -1,6 +1,6 @@ // Copyright (c) 2012-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_LEVELDBWRAPPER_H diff --git a/src/storage/txdb.cpp b/src/storage/txdb.cpp index aac7e851..00aa891c 100644 --- a/src/storage/txdb.cpp +++ b/src/storage/txdb.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "storage/txdb.h" diff --git a/src/storage/txdb.h b/src/storage/txdb.h index aa9d5ad6..3a0a83b7 100644 --- a/src/storage/txdb.h +++ b/src/storage/txdb.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_TXDB_H diff --git a/src/structs/alert.cpp b/src/structs/alert.cpp index aad90188..535cd4f2 100644 --- a/src/structs/alert.cpp +++ b/src/structs/alert.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "structs/alert.h" diff --git a/src/structs/alert.h b/src/structs/alert.h index 59b543ab..0e9e3d69 100644 --- a/src/structs/alert.h +++ b/src/structs/alert.h @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_ALERT_H diff --git a/src/structs/amount.cpp b/src/structs/amount.cpp index 0503637b..93416b3d 100644 --- a/src/structs/amount.cpp +++ b/src/structs/amount.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "structs/amount.h" diff --git a/src/structs/amount.h b/src/structs/amount.h index b894f3fb..b5f9a072 100644 --- a/src/structs/amount.h +++ b/src/structs/amount.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_AMOUNT_H diff --git a/src/structs/base58.cpp b/src/structs/base58.cpp index bbe4b3d8..a3b817c5 100644 --- a/src/structs/base58.cpp +++ b/src/structs/base58.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "structs/base58.h" @@ -67,8 +67,148 @@ bool DecodeBase58(const char* psz, std::vector& vch) vch.push_back(*(it++)); return true; } - std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) +{ + // Skip & count leading zeroes. + int i,j,k; + int zeroes = 0; + while (pbegin != pend && *pbegin == 0) { + pbegin++; + zeroes++; + } + + int b58_size=(pend - pbegin) * 138 / 100 + 1;// log(256) / log(58), rounded up. + int bx_power=5; + int64_t bx_base=656356768;//58^5 + int bx_size=(b58_size-1)/bx_power+1; + int bin_power=4; + int64_t bin_base=4294967296; + int bin_size=(pend - pbegin-1)/bin_power + 1; + + // Allocate enough space in big-endian base58 representation. + std::vector bin(bin_size); + std::vector b58(b58_size); + std::vector bx(bx_size); + + const unsigned char* pfirst=pend-bin_size*bin_power; + while (pbegin != pend) { + if(pbegin >= pfirst) + { + bin[(pbegin-pfirst)/bin_power] |= (*pbegin) << ((bin_power - 1 - (pbegin-pfirst)%bin_power) * 8); + } + pbegin++; + } + + // Process the uint32_ts. + for(i=0;i::reverse_iterator it = bx.rbegin(); it != bx.rend(); it++) { + carry += bin_base * (*it); + *it = carry % bx_base; + carry /= bx_base; + } + assert(carry == 0); +// pbegin++; + } + + k=b58_size; + i=bx_size; + j=0; + int64_t value=0; + while(k > 0) + { + k--; + if(j == 0) + { + i--; + j=bx_power; + value=bx[i]; + } + b58[k]=(unsigned char)(value%58); + value/=58; + j--; + } + + // Skip leading zeroes in base58 result. + std::vector::iterator it = b58.begin(); + while (it != b58.end() && *it == 0) + it++; + // Translate the result into a string. + std::string str; + str.reserve(zeroes + (b58.end() - it)); + str.assign(zeroes, '1'); + while (it != b58.end()) + str += pszBase58[*(it++)]; + return str; +} + +std::string EncodeBase58_19(const unsigned char* pbegin, const unsigned char* pend) +{ + // Skip & count leading zeroes. + int zeroes = 0; + while (pbegin != pend && *pbegin == 0) { + pbegin++; + zeroes++; + } + + int b58_size=(pend - pbegin) * 138 / 100 + 1;// log(256) / log(58), rounded up. + int bx_power=9; + int64_t bx_base=195112;//58^3 + bx_base=bx_base*bx_base*bx_base;//58^9 + + int bx_size=(b58_size-1)/bx_power+1; + // Allocate enough space in big-endian base58 representation. + std::vector b58(b58_size); + std::vector bx(bx_size); + // Process the bytes. + while (pbegin != pend) { + int64_t carry = *pbegin; + // Apply "b58 = b58 * 256 + ch". + for (std::vector::reverse_iterator it = bx.rbegin(); it != bx.rend(); it++) { + carry += 256 * (*it); + *it = carry % bx_base; + carry /= bx_base; + } + assert(carry == 0); + pbegin++; + } + + int i,j,k; + k=b58_size; + i=bx_size; + j=0; + int64_t value=0; + while(k > 0) + { + k--; + if(j == 0) + { + i--; + j=bx_power; + value=bx[i]; + } + b58[k]=(unsigned char)(value%58); + value/=58; + j--; + } + + // Skip leading zeroes in base58 result. + std::vector::iterator it = b58.begin(); + while (it != b58.end() && *it == 0) + it++; + // Translate the result into a string. + std::string str; + str.reserve(zeroes + (b58.end() - it)); + str.assign(zeroes, '1'); + while (it != b58.end()) + str += pszBase58[*(it++)]; + return str; +} + + +std::string EncodeBase58_11(const unsigned char* pbegin, const unsigned char* pend) { // Skip & count leading zeroes. int zeroes = 0; diff --git a/src/structs/base58.h b/src/structs/base58.h index 4e3c379b..af1fee7e 100644 --- a/src/structs/base58.h +++ b/src/structs/base58.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. /** diff --git a/src/structs/bloom.cpp b/src/structs/bloom.cpp index 6e19b976..d664d727 100644 --- a/src/structs/bloom.cpp +++ b/src/structs/bloom.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2012-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "structs/bloom.h" diff --git a/src/structs/bloom.h b/src/structs/bloom.h index 29b85f51..8f6c1435 100644 --- a/src/structs/bloom.h +++ b/src/structs/bloom.h @@ -1,6 +1,6 @@ // Copyright (c) 2012-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_BLOOM_H diff --git a/src/structs/hash.cpp b/src/structs/hash.cpp index b1a5887a..20d7ee50 100644 --- a/src/structs/hash.cpp +++ b/src/structs/hash.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2013-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "structs/hash.h" diff --git a/src/structs/hash.h b/src/structs/hash.h index 6b394d6a..bcc14669 100644 --- a/src/structs/hash.h +++ b/src/structs/hash.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_HASH_H diff --git a/src/structs/limitedmap.h b/src/structs/limitedmap.h index e1f88cfd..11ccf633 100644 --- a/src/structs/limitedmap.h +++ b/src/structs/limitedmap.h @@ -1,6 +1,6 @@ // Copyright (c) 2012-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_LIMITEDMAP_H diff --git a/src/structs/uint256.cpp b/src/structs/uint256.cpp index fa4f9efb..a6a0a35e 100644 --- a/src/structs/uint256.cpp +++ b/src/structs/uint256.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "structs/uint256.h" diff --git a/src/structs/uint256.h b/src/structs/uint256.h index 5647410e..5616919b 100644 --- a/src/structs/uint256.h +++ b/src/structs/uint256.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_UINT256_H diff --git a/src/ui/noui.cpp b/src/ui/noui.cpp index 5d0e2b47..d7755897 100644 --- a/src/ui/noui.cpp +++ b/src/ui/noui.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "ui/noui.h" diff --git a/src/ui/noui.h b/src/ui/noui.h index 0dc69dbb..0d106609 100644 --- a/src/ui/noui.h +++ b/src/ui/noui.h @@ -1,6 +1,6 @@ // Copyright (c) 2013 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_NOUI_H diff --git a/src/ui/ui_interface.h b/src/ui/ui_interface.h index 4e2cea19..8591cfe9 100644 --- a/src/ui/ui_interface.h +++ b/src/ui/ui_interface.h @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2012 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_UI_INTERFACE_H diff --git a/src/univalue/univalue_read.cpp b/src/univalue/univalue_read.cpp index 18362a25..ad348dcd 100644 --- a/src/univalue/univalue_read.cpp +++ b/src/univalue/univalue_read.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include diff --git a/src/univalue/univalue_utffilter.h b/src/univalue/univalue_utffilter.h index 9040274c..e29c09de 100644 --- a/src/univalue/univalue_utffilter.h +++ b/src/univalue/univalue_utffilter.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef UNIVALUE_UTFFILTER_H #define UNIVALUE_UTFFILTER_H diff --git a/src/utils/allocators.cpp b/src/utils/allocators.cpp index 55a96f78..fdc43946 100644 --- a/src/utils/allocators.cpp +++ b/src/utils/allocators.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "utils/allocators.h" diff --git a/src/utils/allocators.h b/src/utils/allocators.h index 90e44fdc..bed01c74 100644 --- a/src/utils/allocators.h +++ b/src/utils/allocators.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_ALLOCATORS_H diff --git a/src/utils/compat.h b/src/utils/compat.h index 4bdfb866..451de249 100644 --- a/src/utils/compat.h +++ b/src/utils/compat.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_COMPAT_H diff --git a/src/utils/compressor.cpp b/src/utils/compressor.cpp index 93c24e39..0b1b134a 100644 --- a/src/utils/compressor.cpp +++ b/src/utils/compressor.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "utils/compressor.h" diff --git a/src/utils/compressor.h b/src/utils/compressor.h index 2fed78bd..c9a7f272 100644 --- a/src/utils/compressor.h +++ b/src/utils/compressor.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_COMPRESSOR_H diff --git a/src/utils/core_io.h b/src/utils/core_io.h index dcd7c6e3..f6a2642c 100644 --- a/src/utils/core_io.h +++ b/src/utils/core_io.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_CORE_IO_H diff --git a/src/utils/core_read.cpp b/src/utils/core_read.cpp index 190a5d15..e17611ad 100644 --- a/src/utils/core_read.cpp +++ b/src/utils/core_read.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "utils/core_io.h" diff --git a/src/utils/core_write.cpp b/src/utils/core_write.cpp index 450df404..99ffe1d5 100644 --- a/src/utils/core_write.cpp +++ b/src/utils/core_write.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "utils/core_io.h" diff --git a/src/utils/dbwrapper.cpp b/src/utils/dbwrapper.cpp index f6897957..480d2590 100644 --- a/src/utils/dbwrapper.cpp +++ b/src/utils/dbwrapper.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. //#include "utils/declare.h" diff --git a/src/utils/dbwrapper.h b/src/utils/dbwrapper.h index 2c00e327..ba6e1408 100644 --- a/src/utils/dbwrapper.h +++ b/src/utils/dbwrapper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAINDB_H diff --git a/src/utils/declare.h b/src/utils/declare.h index e379edfe..fb81f224 100644 --- a/src/utils/declare.h +++ b/src/utils/declare.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAIN_DECLARE_H @@ -285,7 +285,10 @@ const char* __US_UserHomeDir(); char * __US_FullPath(const char* path, char *full_path, int len); void __US_FlushFile(int FileHan); void __US_FlushFileWithMode(int FileHan,uint32_t use_data_sync); +int __US_LockFile(int FileHan); +int __US_UnLockFile(int FileHan); int __US_DeleteFile(const char *file_name); +int __US_GetPID(); void sprintf_hex(char *hex,const unsigned char *bin,int size); diff --git a/src/utils/define.h b/src/utils/define.h index 4fa65322..e44dfcf1 100644 --- a/src/utils/define.h +++ b/src/utils/define.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAIN_DEFINE_H @@ -61,6 +61,7 @@ #define MC_FOM_NONE 0x00000000 #define MC_FOM_RELATIVE_TO_DATADIR 0x00000001 +#define MC_FOM_RELATIVE_TO_LOGDIR 0x00000002 #define MC_FOM_RELATIVE_MASK 0x0000000F #define MC_FOM_CREATE_DIR 0x00000100 diff --git a/src/utils/mruset.h b/src/utils/mruset.h index cd55abc2..8c2533c0 100644 --- a/src/utils/mruset.h +++ b/src/utils/mruset.h @@ -1,6 +1,6 @@ // Copyright (c) 2012 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_MRUSET_H diff --git a/src/utils/random.cpp b/src/utils/random.cpp index d65b69ff..a24c33d6 100644 --- a/src/utils/random.cpp +++ b/src/utils/random.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "utils/random.h" diff --git a/src/utils/random.h b/src/utils/random.h index d619acf3..4782f098 100644 --- a/src/utils/random.h +++ b/src/utils/random.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_RANDOM_H diff --git a/src/utils/serialize.h b/src/utils/serialize.h index 5b70d97b..133c9837 100644 --- a/src/utils/serialize.h +++ b/src/utils/serialize.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_SERIALIZE_H @@ -352,7 +352,7 @@ class CFlatData unsigned int GetSerializeSize(int, int=0) const { - return pend - pbegin; + return static_cast(pend - pbegin); } template diff --git a/src/utils/streams.h b/src/utils/streams.h index 246a2026..b30e65a3 100644 --- a/src/utils/streams.h +++ b/src/utils/streams.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_STREAMS_H diff --git a/src/utils/sync.cpp b/src/utils/sync.cpp index 1e29a673..1141475b 100644 --- a/src/utils/sync.cpp +++ b/src/utils/sync.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2012 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "utils/sync.h" diff --git a/src/utils/sync.h b/src/utils/sync.h index 1edd9f47..6ffa5b6d 100644 --- a/src/utils/sync.h +++ b/src/utils/sync.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_SYNC_H diff --git a/src/utils/systemdependent.cpp b/src/utils/systemdependent.cpp index b8921862..4983c3e8 100644 --- a/src/utils/systemdependent.cpp +++ b/src/utils/systemdependent.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "multichain/multichain.h" @@ -245,11 +245,25 @@ void __US_FlushFileWithMode(int FileHan,uint32_t use_data_sync) } } +int __US_LockFile(int FileHan) +{ + return flock(FileHan,LOCK_EX); +} + +int __US_UnLockFile(int FileHan) +{ + return flock(FileHan,LOCK_UN); +} + int __US_DeleteFile(const char *file_name) { return unlink(file_name); } +int __US_GetPID() +{ + return getpid(); +} #else #include "windows.h" @@ -331,10 +345,39 @@ void __US_FlushFileWithMode(int FileHan,uint32_t use_data_sync) FlushFileBuffers(hFile); } +int __US_LockFile(int FileHan) +{ + HANDLE hFile = (HANDLE)_get_osfhandle(FileHan); + OVERLAPPED overlapvar = { 0 }; + + if(LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, + 0, MAXDWORD, MAXDWORD, &overlapvar)) + { + return 0; + } + return -1; +} + +int __US_UnLockFile(int FileHan) +{ + HANDLE hFile = (HANDLE)_get_osfhandle(FileHan); + OVERLAPPED overlapvar = { 0 }; + + if(UnlockFileEx(hFile, 0, MAXDWORD, MAXDWORD, &overlapvar)) + { + return 0; + } + return -1; +} + int __US_DeleteFile(const char *file_name) { return (int)DeleteFile(file_name); } +int __US_GetPID() +{ + return (int)GetCurrentProcessId(); +} #endif diff --git a/src/utils/threadsafety.h b/src/utils/threadsafety.h index e772e4c2..e0858511 100644 --- a/src/utils/threadsafety.h +++ b/src/utils/threadsafety.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Original code was distributed under the MIT/X11 software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_THREADSAFETY_H diff --git a/src/utils/timedata.cpp b/src/utils/timedata.cpp index 623bbfb8..e706c936 100644 --- a/src/utils/timedata.cpp +++ b/src/utils/timedata.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "utils/timedata.h" diff --git a/src/utils/timedata.h b/src/utils/timedata.h index 7c40b4a8..3bcc97f1 100644 --- a/src/utils/timedata.h +++ b/src/utils/timedata.h @@ -1,6 +1,6 @@ // Copyright (c) 2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_TIMEDATA_H diff --git a/src/utils/tinyformat.h b/src/utils/tinyformat.h index 73d49a1f..f69342e5 100644 --- a/src/utils/tinyformat.h +++ b/src/utils/tinyformat.h @@ -783,23 +783,27 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, break; case 'X': out.setf(std::ios::uppercase); + // fall through case 'x': case 'p': out.setf(std::ios::hex, std::ios::basefield); intConversion = true; break; case 'E': out.setf(std::ios::uppercase); + // fall through case 'e': out.setf(std::ios::scientific, std::ios::floatfield); out.setf(std::ios::dec, std::ios::basefield); break; case 'F': out.setf(std::ios::uppercase); + // fall through case 'f': out.setf(std::ios::fixed, std::ios::floatfield); break; case 'G': out.setf(std::ios::uppercase); + // fall through case 'g': out.setf(std::ios::dec, std::ios::basefield); // As in boost::format, let stream decide float format. diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index ede25ea9..33d50d20 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "multichain/multichain.h" diff --git a/src/utils/util.cpp b/src/utils/util.cpp index e39a9bfa..ad995bc0 100644 --- a/src/utils/util.cpp +++ b/src/utils/util.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #if defined(HAVE_CONFIG_H) @@ -177,7 +177,7 @@ static void DebugPrintInit() assert(fileout == NULL); assert(mutexDebugLog == NULL); - boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + boost::filesystem::path pathDebug = GetLogDir() / "debug.log"; fileout = fopen(pathDebug.string().c_str(), "a"); if (fileout) setbuf(fileout, NULL); // unbuffered @@ -247,7 +247,7 @@ int LogPrintStr(const std::string &str) // reopen the log file, if requested if (fReopenDebugLog) { fReopenDebugLog = false; - boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + boost::filesystem::path pathDebug = GetLogDir() / "debug.log"; if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL) setbuf(fileout, NULL); // unbuffered } @@ -413,6 +413,7 @@ void PrintExceptionContinue(std::exception* pex, const char* pszThread) /* MCHN START */ static boost::filesystem::path pathCachedMultiChain; +static boost::filesystem::path pathCachedMultiChainLog; static CCriticalSection csPathCached; /* MCHN END */ @@ -457,6 +458,19 @@ static boost::filesystem::path pathCached; static boost::filesystem::path pathCachedNetSpecific; //static CCriticalSection csPathCached; +const boost::filesystem::path &GetLogDir(bool fNetSpecific) +{ + namespace fs = boost::filesystem; + + LOCK(csPathCached); + +/* MCHN START */ + fs::path &path =pathCachedMultiChainLog; + if (!path.empty()) + return path; + path=fs::path(string(mc_gState->m_Params->DataDir(2,1))); + return pathCachedMultiChainLog; +} const boost::filesystem::path &GetDataDir(bool fNetSpecific) { @@ -538,7 +552,7 @@ void ReadConfigFile(map& mapSettingsRet, ClearDatadirCache(); } -#ifndef WIN32 +//#ifndef WIN32 boost::filesystem::path GetPidFile() { boost::filesystem::path pathPidFile(GetArg("-pid", "multichain.pid")); @@ -546,7 +560,7 @@ boost::filesystem::path GetPidFile() return pathPidFile; } -void CreatePidFile(const boost::filesystem::path &path, pid_t pid) +void CreatePidFile(const boost::filesystem::path &path, int pid) { FILE* file = fopen(path.string().c_str(), "w"); if (file) @@ -555,7 +569,7 @@ void CreatePidFile(const boost::filesystem::path &path, pid_t pid) fclose(file); } } -#endif +//#endif bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest) { @@ -684,7 +698,7 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { void ShrinkDebugFile(const char* FileName) { // Scroll debug.log if it's getting too big - boost::filesystem::path pathLog = GetDataDir() / string(FileName); + boost::filesystem::path pathLog = GetLogDir() / string(FileName); FILE* file = fopen(pathLog.string().c_str(), "r"); /* MCHN START */ size_t bytes_written; @@ -727,7 +741,7 @@ void ShrinkDebugFile() ShrinkDebugFile("wallet/txs.log"); // Scroll debug.log if it's getting too big /* - boost::filesystem::path pathLog = GetDataDir() / "debug.log"; + boost::filesystem::path pathLog = GetLogDir() / "debug.log"; FILE* file = fopen(pathLog.string().c_str(), "r"); int64_t shrink_size=GetArg("-shrinkdebugfilesize",200000); if(shrink_size > 67108864) @@ -925,3 +939,23 @@ std::string mc_BuildDescription(int build) mc_BuildDescription(build,build_desc); return std::string(build_desc); } + +bool mc_CopyFile(boost::filesystem::path& pathDBOld,boost::filesystem::path& pathDBNew) +{ +#ifndef WIN32 + + try { +#if BOOST_VERSION >= 104000 + boost::filesystem::copy_file(pathDBOld, pathDBNew, boost::filesystem::copy_option::overwrite_if_exists); +#else + filesystem::copy_file(pathSrc, pathDest); +#endif + } catch(const boost::filesystem::filesystem_error &e) { + LogPrintf("error copying %s to %s - %s\n", pathDBOld.string(), pathDBNew.string(), e.what()); + return false; + } + return true; +#else + return CopyFile(pathDBOld.string().c_str(),pathDBNew.string().c_str(),false); +#endif +} \ No newline at end of file diff --git a/src/utils/util.h b/src/utils/util.h index 710a711e..fc720e0d 100644 --- a/src/utils/util.h +++ b/src/utils/util.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. /** @@ -99,11 +99,12 @@ std::string mc_BuildDescription(int build); boost::filesystem::path GetDefaultDataDir(); const boost::filesystem::path &GetDataDir(bool fNetSpecific = true); +const boost::filesystem::path &GetLogDir(bool fNetSpecific = true); boost::filesystem::path GetConfigFile(); -#ifndef WIN32 +//#ifndef WIN32 boost::filesystem::path GetPidFile(); -void CreatePidFile(const boost::filesystem::path &path, pid_t pid); -#endif +void CreatePidFile(const boost::filesystem::path &path, int pid); +//#endif void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); #ifdef WIN32 boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); diff --git a/src/utils/utility.cpp b/src/utils/utility.cpp index 3ebf0d70..809bf163 100644 --- a/src/utils/utility.cpp +++ b/src/utils/utility.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "multichain/multichain.h" diff --git a/src/utils/utilmoneystr.cpp b/src/utils/utilmoneystr.cpp index 964b8cb3..5716aad0 100644 --- a/src/utils/utilmoneystr.cpp +++ b/src/utils/utilmoneystr.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "utilmoneystr.h" diff --git a/src/utils/utilmoneystr.h b/src/utils/utilmoneystr.h index 21b8caa7..428ef17d 100644 --- a/src/utils/utilmoneystr.h +++ b/src/utils/utilmoneystr.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. /** diff --git a/src/utils/utilparse.cpp b/src/utils/utilparse.cpp index 38a53844..5cbb45a7 100644 --- a/src/utils/utilparse.cpp +++ b/src/utils/utilparse.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "utils/utilparse.h" @@ -121,6 +121,30 @@ bool mc_VerifyAssetPermissions(mc_Buffer *assets, vector address return true; } +bool HasPerOutputDataEntries(const CTxOut& txout,mc_Script *lpScript) +{ + if(mc_gState->m_NetworkParams->IsProtocolMultichain()) + { + unsigned char *ptr; + int size; + const CScript& script1 = txout.scriptPubKey; + CScript::const_iterator pc1 = script1.begin(); + lpScript->Clear(); + lpScript->SetScript((unsigned char*)(&pc1[0]),(size_t)(script1.end()-pc1),MC_SCR_TYPE_SCRIPTPUBKEY); + + for (int e = 0; e < lpScript->GetNumElements(); e++) + { + lpScript->SetElement(e); + if(lpScript->GetRawData(&ptr,&size) == 0) + { + return true; + } + } + } + + return false; +} + /* * Parses txout script into asset-quantity buffer @@ -492,7 +516,10 @@ bool ParseMultichainTxOutToBuffer(uint256 hash, { if(mc_gState->m_NetworkParams->GetInt64Param("supportminerprecheck")) { - *required |= MC_PTP_CACHED_SCRIPT_REQUIRED; + if(entity.GetEntityType() == MC_ENT_TYPE_NONE) + { + *required |= MC_PTP_CACHED_SCRIPT_REQUIRED; + } } } diff --git a/src/utils/utilparse.h b/src/utils/utilparse.h index 41c76e8e..71a4cb0d 100644 --- a/src/utils/utilparse.h +++ b/src/utils/utilparse.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAINUTILS_H @@ -12,6 +12,7 @@ bool ExtractDestinationScriptValid(const CScript& scriptPubKey, CTxDestination& addressRet); const unsigned char* GetAddressIDPtr(const CTxDestination& address); +bool HasPerOutputDataEntries(const CTxOut& txout,mc_Script *lpScript); bool ParseMultichainTxOutToBuffer(uint256 hash,const CTxOut& txout,mc_Buffer *amounts,mc_Script *lpScript,int *allowed,int *required,std::map* mapSpecialEntity,std::string& strFailReason); bool ParseMultichainTxOutToBuffer(uint256 hash,const CTxOut& txout,mc_Buffer *amounts,mc_Script *lpScript,int *allowed,int *required,std::string& strFailReason); bool CreateAssetBalanceList(const CTxOut& txout,mc_Buffer *amounts,mc_Script *lpScript,int *required); diff --git a/src/utils/utilstrencodings.cpp b/src/utils/utilstrencodings.cpp index 205b1d75..10ad1bbe 100644 --- a/src/utils/utilstrencodings.cpp +++ b/src/utils/utilstrencodings.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "utils/utilstrencodings.h" diff --git a/src/utils/utilstrencodings.h b/src/utils/utilstrencodings.h index 79018470..72b7c3ce 100644 --- a/src/utils/utilstrencodings.h +++ b/src/utils/utilstrencodings.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. /** diff --git a/src/utils/utiltime.cpp b/src/utils/utiltime.cpp index 87e36112..5c8341bc 100644 --- a/src/utils/utiltime.cpp +++ b/src/utils/utiltime.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #if defined(HAVE_CONFIG_H) diff --git a/src/utils/utiltime.h b/src/utils/utiltime.h index 9959d56c..3d485eeb 100644 --- a/src/utils/utiltime.h +++ b/src/utils/utiltime.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef BITCOIN_UTILTIME_H diff --git a/src/utils/utilwrapper.cpp b/src/utils/utilwrapper.cpp index 66ff33bb..8f23114d 100644 --- a/src/utils/utilwrapper.cpp +++ b/src/utils/utilwrapper.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Original code was distributed under the MIT software license. -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2019 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #include "multichain/multichain.h" @@ -71,6 +71,7 @@ using namespace std; const boost::filesystem::path mc_GetDataDir(const char *network_name,int create); +const boost::filesystem::path mc_GetLogDir(const char *network_name,int create); void mc_Params::Parse(int argc, const char* const argv[],int exe_type) { @@ -243,12 +244,26 @@ const char *mc_Params::DataDir(int network_specific,int create) name=NetworkName(); } - boost::filesystem::path path=mc_GetDataDir(name,create); + boost::filesystem::path path; if(network_specific) { - strcpy(m_DataDirNetSpecific,path.string().c_str()); - return m_DataDirNetSpecific; + if(network_specific == 1) + { + path=mc_GetDataDir(name,create); + strcpy(m_DataDirNetSpecific,path.string().c_str()); + return m_DataDirNetSpecific; + } + else + { + path=mc_GetLogDir(name,create); + strcpy(m_LogDirNetSpecific,path.string().c_str()); + return m_LogDirNetSpecific; + } + } + else + { + path=mc_GetDataDir(name,create); } strcpy(m_DataDir,path.string().c_str()); @@ -342,6 +357,22 @@ void mc_ExpandDataDirParam() } } } + if (mapArgs.count("-logdir")) + { + string original=mapArgs["-logdir"]; + if(original.size() > 1) + { + if( (*(original.c_str()) == '~') && (*(original.c_str() + 1) == '/') ) + { + const char *homedir=__US_UserHomeDir(); + + if(homedir) + { + mapArgs["-logdir"]=strprintf("%s%s",homedir,original.c_str()+1); + } + } + } + } } void mc_CheckDataDirInConfFile() @@ -390,6 +421,31 @@ const boost::filesystem::path mc_GetDataDir(const char *network_name,int create) return path; } +const boost::filesystem::path mc_GetLogDir(const char *network_name,int create) +{ + boost::filesystem::path path; + if (mapArgs.count("-logdir")) { + path = boost::filesystem::system_complete(mapArgs["-logdir"]); + if (!boost::filesystem::is_directory(path)) + { + return path; + } + } + else + { + return mc_GetDataDir(network_name,create); + } + if(network_name) + { + path /= std::string(network_name); + } + if(create) + { + boost::filesystem::create_directories(path); + } + return path; +} + void mc_CreateDir(const char *dir_name) { boost::filesystem::create_directories(boost::filesystem::path(dir_name)); @@ -456,6 +512,9 @@ string mc_GetFullFileName(const char *network_name,const char *filename, const c case MC_FOM_RELATIVE_TO_DATADIR: pathFile = mc_GetDataDir(network_name,create) / fullName; break; + case MC_FOM_RELATIVE_TO_LOGDIR: + pathFile = mc_GetLogDir(network_name,create) / fullName; + break; } return pathFile.string(); diff --git a/src/v8/callbacks.cpp b/src/v8/callbacks.cpp new file mode 100644 index 00000000..beff0e09 --- /dev/null +++ b/src/v8/callbacks.cpp @@ -0,0 +1,85 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "v8/callbacks.h" +#include "v8/v8engine.h" +#include "v8/v8json_spirit.h" +#include "v8/v8utils.h" +#include + +namespace mc_v8 +{ +/** + * Call an RPC function from a V8 JS callback. + * + * Marshal the arguments and the return value between V8 and json_spirit using intermediate JSON strings. + * Optionally filter the result before returning it to JS. + * + * @param name The name of the RPC function. + * @param args The V8 arguments/return value. + */ +void CallRpcFunction(std::string name, const v8::FunctionCallbackInfo &args) +{ + v8::Isolate *isolate = args.GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Local context(isolate->GetCurrentContext()); + v8::Context::Scope contextScope(context); + + IFilterCallback *filterCallback = static_cast(args.Data().As()->Value()); + + json_spirit::Array params; + for (int i = 0; i < args.Length(); ++i) + { + params.push_back(V82Jsp(isolate, args[i])); + } + + json_spirit::Value result; + filterCallback->JspCallback(name, params, result); + if (result.is_null()) + { + args.GetReturnValue().SetUndefined(); + } + else + { + args.GetReturnValue().Set(Jsp2V8(isolate, result)); + } +} + +// clang-format off +#define FILTER_FUNCTION(name) \ + void filter_##name(const v8::FunctionCallbackInfo &args) \ + { \ + CallRpcFunction(#name, args); \ + } + +FILTER_FUNCTION(getfiltertxid) +FILTER_FUNCTION(getfiltertransaction) +FILTER_FUNCTION(getfilterstreamitem) +FILTER_FUNCTION(getfilterassetbalances) +FILTER_FUNCTION(setfilterparam) +FILTER_FUNCTION(getfiltertxinput) +FILTER_FUNCTION(getlastblockinfo) +FILTER_FUNCTION(getassetinfo) +FILTER_FUNCTION(getstreaminfo) +FILTER_FUNCTION(verifypermission) +FILTER_FUNCTION(verifymessage) + +#define FILTER_LOOKUP(name) { #name, filter_##name } + +std::map callbackLookup{ + FILTER_LOOKUP(getfiltertxid), + FILTER_LOOKUP(getfiltertransaction), + FILTER_LOOKUP(getfilterstreamitem), + FILTER_LOOKUP(getfilterassetbalances), + FILTER_LOOKUP(setfilterparam), + FILTER_LOOKUP(getfiltertxinput), + FILTER_LOOKUP(getlastblockinfo), + FILTER_LOOKUP(getassetinfo), + FILTER_LOOKUP(getstreaminfo), + FILTER_LOOKUP(verifypermission), + FILTER_LOOKUP(verifymessage) +}; +// clang-format on +} // namespace mc_v8 diff --git a/src/v8/callbacks.h b/src/v8/callbacks.h new file mode 100644 index 00000000..80369634 --- /dev/null +++ b/src/v8/callbacks.h @@ -0,0 +1,16 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef MULTICHAIN_CALLBACKS_H_ +#define MULTICHAIN_CALLBACKS_H_ + +#include +#include + +namespace mc_v8 +{ +extern std::map callbackLookup; + +} // namespace mc_v8 + +#endif /* MULTICHAIN_CALLBACKS_H_ */ diff --git a/src/v8/callbacks_win.cpp b/src/v8/callbacks_win.cpp new file mode 100644 index 00000000..f9870b25 --- /dev/null +++ b/src/v8/callbacks_win.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "v8/callbacks.h" +#include "v8/v8engine.h" +#include "v8/v8utils.h" +#include "v8/v8ubjson.h" +#include "v8/v8blob.h" +#include + +namespace mc_v8 +{ +/** + * Call an RPC function from a V8 JS callback. + * + * Marshal the arguments and the return value between V8 and json_spirit using intermediate JSON strings. + * Optionally filter the result before returning it to JS. + * + * @param name The name of the RPC function. + * @param args The V8 arguments/return value. + */ +void CallRpcFunction(std::string name, const v8::FunctionCallbackInfo &args) +{ + v8::Isolate *isolate = args.GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Local context(isolate->GetCurrentContext()); + v8::Context::Scope contextScope(context); + + IFilterCallback *filterCallback = static_cast(args.Data().As()->Value()); + + auto v8args = v8::Array::New(isolate, args.Length()); + for (int i = 0; i < args.Length(); ++i) + { + v8args->Set(static_cast(i), args[i]); + } + + BlobPtr argsBlob = Blob::Instance("args"); + BlobPtr resultBlob = Blob::Instance("result"); + V82Ubj(isolate, v8args, argsBlob); + unsigned char *result; + size_t resultSize; + + filterCallback->UbjCallback(name.c_str(), argsBlob->Data(), &result, &resultSize); + resultBlob->Set(result, resultSize); + + if (resultBlob->IsEmpty()) + { + args.GetReturnValue().SetUndefined(); + } + else + { + int err; + args.GetReturnValue().Set(Ubj2V8(isolate, resultBlob, &err)); + } + + if (result != nullptr) + { + delete [] result; + } +} + +// clang-format off +#define FILTER_FUNCTION(name) \ + void filter_##name(const v8::FunctionCallbackInfo &args) \ + { \ + CallRpcFunction(#name, args); \ + } + +FILTER_FUNCTION(getfiltertxid) +FILTER_FUNCTION(getfiltertransaction) +FILTER_FUNCTION(getfilterstreamitem) +FILTER_FUNCTION(getfilterassetbalances) +FILTER_FUNCTION(setfilterparam) +FILTER_FUNCTION(getfiltertxinput) +FILTER_FUNCTION(getlastblockinfo) +FILTER_FUNCTION(getassetinfo) +FILTER_FUNCTION(getstreaminfo) +FILTER_FUNCTION(verifypermission) +FILTER_FUNCTION(verifymessage) + +#define FILTER_LOOKUP(name) { #name, filter_##name } + +std::map callbackLookup{ + FILTER_LOOKUP(getfiltertxid), + FILTER_LOOKUP(getfiltertransaction), + FILTER_LOOKUP(getfilterstreamitem), + FILTER_LOOKUP(getfilterassetbalances), + FILTER_LOOKUP(setfilterparam), + FILTER_LOOKUP(getfiltertxinput), + FILTER_LOOKUP(getlastblockinfo), + FILTER_LOOKUP(getassetinfo), + FILTER_LOOKUP(getstreaminfo), + FILTER_LOOKUP(verifypermission), + FILTER_LOOKUP(verifymessage) +}; +// clang-format on +} // namespace mc_v8 diff --git a/src/v8/fixture.js b/src/v8/fixture.js new file mode 100644 index 00000000..c1aef7e5 --- /dev/null +++ b/src/v8/fixture.js @@ -0,0 +1,33 @@ +Math.random = function(){ + return 0; +}; + +Date.now = function() { + return 0; +}; + +var bind = Function.bind; +var unbind = bind.bind(bind); + +function instantiate(constructor, args) { + return new (unbind(constructor, null).apply(null, args)); +} + +Date = function (Date) { + var names = Object.getOwnPropertyNames(Date); + for (var i = 0; i < names.length; i++) { + // Skip props already in the MyDate object + if (names[i] in MyDate) continue; + var desc = Object.getOwnPropertyDescriptor(Date, names[i]); + Object.defineProperty(MyDate, names[i], desc); + } + + return MyDate; + + function MyDate() { + if (arguments.length == 0) { + arguments = [0]; + } + return instantiate(Date, arguments); + } +}(Date); diff --git a/src/v8/v8blob.cpp b/src/v8/v8blob.cpp new file mode 100644 index 00000000..27bef007 --- /dev/null +++ b/src/v8/v8blob.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "v8blob.h" +#include + +namespace mc_v8 +{ +BlobPtr Blob::Instance(std::string name) +{ + auto it = m_instances.find(name); + if (it == m_instances.end()) + { + it = m_instances.insert(std::make_pair(name, std::make_shared())).first; + } + return it->second; +} + +Blob::~Blob() +{ + if (m_buffer != nullptr) + { + delete[] m_buffer; + } +} + +void Blob::Reset() +{ + m_size = 0; +} + +void Blob::Set(void *data, size_t size) +{ + this->Reset(); + this->Append(data, size); +} + +void Blob::Append(void *data, size_t size) +{ + this->Resize(size); + std::memcpy(m_buffer + m_size, data, size); + m_size += size; +} + +void Blob::Remove(std::string name) +{ + m_instances.erase(name); +} + +void Blob::Resize(size_t add_size) +{ + if (m_size + add_size > m_allocated) + { + size_t new_size = ((m_size + add_size - 1) / size_increment + 1) * size_increment; + auto new_buffer = new unsigned char[new_size]; + if (m_allocated > 0) + { + std::memcpy(new_buffer, m_buffer, m_allocated); + delete[] m_buffer; + } + m_buffer = new_buffer; + m_allocated = new_size; + } +} + +std::map Blob::m_instances; +} // namespace mc_v8 diff --git a/src/v8/v8blob.h b/src/v8/v8blob.h new file mode 100644 index 00000000..69ca3b4c --- /dev/null +++ b/src/v8/v8blob.h @@ -0,0 +1,63 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef V8BLOB_H +#define V8BLOB_H + +#include "v8_win/declspec.h" +#include +#include +#include + +namespace mc_v8 +{ +class V8_WIN_EXPORTS Blob; +using BlobPtr = std::shared_ptr; + +class V8_WIN_EXPORTS Blob +{ + public: + const size_t size_increment = 4096; + + static BlobPtr Instance(std::string name = ""); + ~Blob(); + + void Reset(); + + void Set(void *data, size_t size); + + void Append(void *data, size_t size); + + unsigned char *Data() + { + return m_buffer; + } + + size_t DataSize() const + { + return m_size; + } + + bool IsEmpty() const + { + return m_size == 0; + } + + static void Remove(std::string name); + + private: + Blob() + { + } + + void Resize(size_t add_size); + + static std::map m_instances; + + unsigned char *m_buffer = nullptr; + size_t m_size = 0; + size_t m_allocated = 0; +}; +} // namespace mc_v8 + +#endif // V8BLOB_H diff --git a/src/v8/v8engine.cpp b/src/v8/v8engine.cpp new file mode 100644 index 00000000..43e1cfd9 --- /dev/null +++ b/src/v8/v8engine.cpp @@ -0,0 +1,104 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "v8/v8engine.h" +#include "utils/define.h" +#include "utils/util.h" +#include "v8/v8filter.h" +#include "v8/v8utils.h" +#include + +extern char _binary_icudtl_dat_start; +extern char _binary_icudtl_dat_end; +extern char _binary_natives_blob_bin_start; +extern char _binary_natives_blob_bin_end; +extern char _binary_snapshot_blob_bin_start; +extern char _binary_snapshot_blob_bin_end; + +namespace mc_v8 +{ +V8Engine::~V8Engine() +{ + if (m_isolate != nullptr) + { + m_isolate->Dispose(); + delete m_createParams.array_buffer_allocator; + } +} + +int V8Engine::Initialize(IFilterCallback *filterCallback, std::string &strResult) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: V8Engine::Initialize\n"); + strResult.clear(); + if (!m_isV8Initialized) + { + this->InitializeV8(); + m_isV8Initialized = true; + } + m_createParams.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + m_isolate = v8::Isolate::New(m_createParams); + m_filterCallback = filterCallback; + return MC_ERR_NOERROR; +} + +int V8Engine::CreateFilter(std::string script, std::string main_name, std::vector &callback_names, + V8Filter *filter, std::string &strResult) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: V8Engine::CreateFilter\n"); + strResult.clear(); + return filter->Initialize(this, script, main_name, callback_names, strResult); +} + +int V8Engine::RunFilter(V8Filter *filter, std::string &strResult) +{ + strResult.clear(); + if (filter == nullptr) + { + strResult = "Trying to run an invalid filter"; + return MC_ERR_NOERROR; + } + return filter->Run(strResult); +} + +void V8Engine::TerminateFilter(V8Filter *filter, std::string reason) +{ + if (filter != nullptr && filter->IsRunning()) + { + m_reason = reason; + m_isolate->TerminateExecution(); + } +} + +void V8Engine::InitializeV8() +{ + assert(!m_isV8Initialized); + fs::path tempDir = GetTemporaryPidDirectory(); + fs::path v8TempDir = tempDir / "v8"; + fs::create_directories(v8TempDir); + + fs::path icudtl_blob = v8TempDir / "icudtl.dat"; + fs::path natives_blob = v8TempDir / "natives_blob.bin"; + fs::path snapshot_blob = v8TempDir / "snapshot_blob.bin"; + + WriteBinaryFile(icudtl_blob, &_binary_icudtl_dat_start, &_binary_icudtl_dat_end - &_binary_icudtl_dat_start); + WriteBinaryFile(natives_blob, &_binary_natives_blob_bin_start, + &_binary_natives_blob_bin_end - &_binary_natives_blob_bin_start); + WriteBinaryFile(snapshot_blob, &_binary_snapshot_blob_bin_start, + &_binary_snapshot_blob_bin_end - &_binary_snapshot_blob_bin_start); + + v8::V8::InitializeICUDefaultLocation(icudtl_blob.string().c_str()); + v8::V8::InitializeExternalStartupData(natives_blob.string().c_str()); + + fs::remove_all(tempDir); + + m_platform = v8::platform::NewDefaultPlatform(); + v8::V8::InitializePlatform(m_platform.get()); + v8::V8::Initialize(); +} + +std::unique_ptr V8Engine::m_platform; +v8::Isolate::CreateParams V8Engine::m_createParams; +bool V8Engine::m_isV8Initialized = false; +} // namespace mc_v8 diff --git a/src/v8/v8engine.h b/src/v8/v8engine.h new file mode 100644 index 00000000..f8b8f89b --- /dev/null +++ b/src/v8/v8engine.h @@ -0,0 +1,91 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef MULTICHAIN_V8ENGINE_H_ +#define MULTICHAIN_V8ENGINE_H_ + +#include "filters/ifiltercallback.h" +#include "rpc/rpcserver.h" +//#include "json/json_spirit.h" +#include + +namespace mc_v8 +{ +class V8Filter; + +/** + * Interface to the Google V8 engine to create and run filters. + */ +class V8Engine +{ + public: + ~V8Engine(); + + /** + * Initialize the V8 engine. + * + * @param strResult Reason for failure if unsuccessful. + * @return MC_ERR_NOERROR if successful, MC_ERR_INTERNAL_ERROR if not. + */ + int Initialize(IFilterCallback *filterCallback, std::string &strResult); + + v8::Isolate *GetIsolate() + { + return m_isolate; + } + + IFilterCallback *GetFilterCallback() + { + return m_filterCallback; + } + + /** + * Create a new filter. + * + * @param script The filter JS code. + * @param main_name The expected name of the filtering function in the script. + * @param callback_names A list of callback function names to register for the filter. + * If empty, register no callback functions. + * @param filter The filter object to initialize. + * @param strResult Reason for failure if unsuccessful. + * @return MC_ERR_INTERNAL_ERROR if the engine failed, MC_ERR_NOERROR otherwise. + */ + int CreateFilter(std::string script, std::string main_name, std::vector &callback_names, + V8Filter *filter, std::string &strResult); + + /** + * Run the filter function in the JS script. + * + * @param filter The user-defined filter to use. + * @param strResult Reason for script failure or rejection. + * @return MC_ERR_INTERNAL_ERROR if the engine failed, MC_ERR_NOERROR otherwise. + */ + int RunFilter(V8Filter *filter, std::string &strResult); + + /** + * Abort the currently running filter (if any). + * + * @param filter The filter to abort. + * @param reason The reason the filter is being terminated. + */ + void TerminateFilter(V8Filter *filter, std::string reason); + + std::string TerminationReason() const + { + return m_reason; + } + + private: + IFilterCallback *m_filterCallback = nullptr; + v8::Isolate *m_isolate = nullptr; + static std::unique_ptr m_platform; + static v8::Isolate::CreateParams m_createParams; + static bool m_isV8Initialized; + std::string m_reason; + + static void InitializeV8(); +}; + +} // namespace mc_v8 + +#endif /* MULTICHAIN_V8ENGINE_H_ */ diff --git a/src/v8/v8filter.cpp b/src/v8/v8filter.cpp new file mode 100644 index 00000000..710a4462 --- /dev/null +++ b/src/v8/v8filter.cpp @@ -0,0 +1,359 @@ +// Copyright (c) 2014-2019 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "v8/v8filter.h" +#include "chainparams/state.h" +//#include "utils/define.h" +#include "utils/tinyformat.h" +#include "utils/util.h" +#include "v8/callbacks.h" +#include "v8/v8engine.h" +#include "v8/v8utils.h" +#include + +#define MC_ERR_NOERROR 0x00000000 +#define MC_ERR_INTERNAL_ERROR 0x00000006 + +/* Unlocked date functions: + +//Date.prototype.getTime = function() {return 0;}; + +//Date.prototype.getUTCDate = function() {return 0;}; +//Date.prototype.getUTCFullYear = function() {return 0;}; +//Date.prototype.getUTCHours = function() {return 0;}; +//Date.prototype.getUTCMonth = function() {return 0;}; +//Date.prototype.getUTCMinutes = function() {return 0;}; +//Date.prototype.getUTCDay = function() {return 0;}; +//Date.prototype.getUTCSeconds = function() {return 0;}; +//Date.prototype.getUTCMilliseconds = function() {return 0;}; +//Date.prototype.getUTCTime = function() {return 0;}; + +//Date.prototype.toGMTString = function() {return 0;}; +//Date.prototype.toISOString = function() {return 0;}; +//Date.prototype.toJSON = function() {return 0;}; +//Date.prototype.toUTCString = function() {return 0;}; +//Date.prototype.valueOf = function() {return 0;}; + +//Date.prototype.setTime = function() {return 0;}; +//Date.prototype.setUTCDate = function() {return 0;}; +//Date.prototype.setUTCFullYear = function() {return 0;}; +//Date.prototype.setUTCHours = function() {return 0;}; +//Date.prototype.setUTCMinutes = function() {return 0;}; +//Date.prototype.setUTCMonth = function() {return 0;}; +//Date.prototype.setUTCYear = function() {return 0;}; +//Date.prototype.setUTCSeconds = function() {return 0;}; +//Date.prototype.setUTCMilliseconds = function() {return 0;}; +//Date.prototype.setUTCTime = function() {return 0;}; +}; +*/ + + +namespace mc_v8 +{ +static std::string jsFixture = R"( +Math.random = function() { + return 0; +}; + +Date.now = function() { + return 0; +}; + +var bind = Function.bind; +var unbind = bind.bind(bind); + +function instantiate(constructor, args) { + return new (unbind(constructor, null).apply(null, args)); +} + +Date = function (Date) { + var names = Object.getOwnPropertyNames(Date); + // Loop through them + for (var i = 0; i < names.length; i++) { + // Skip props already in the MyDate object + if (names[i] in MyDate) continue; + // Get property description from o + var desc = Object.getOwnPropertyDescriptor(Date, names[i]); + // Use it to create property on MyDate + Object.defineProperty(MyDate, names[i], desc); + } + + return MyDate; + + function MyDate() { + if (arguments.length == 0) { + arguments = [0]; + } + return instantiate(Date, arguments); + } +}(Date); +)"; + +static std::string jsFixtureDateFunctions = R"( +Math.random = function() { + return 0; +}; + +Date.now = function() { + return 0; +}; + +var bind = Function.bind; +var unbind = bind.bind(bind); + +function instantiate(constructor, args) { + return new (unbind(constructor, null).apply(null, args)); +} + +Date.prototype.getDate = function() {return 0;}; +Date.prototype.getFullYear = function() {return 0;}; +Date.prototype.getHours = function() {return 0;}; +Date.prototype.getMonth = function() {return 0;}; +Date.prototype.getMinutes = function() {return 0;}; +Date.prototype.getDay = function() {return 0;}; +Date.prototype.getYear = function() {return 0;}; +Date.prototype.getSeconds = function() {return 0;}; +Date.prototype.getMilliseconds = function() {return 0;}; +Date.prototype.getTimezoneOffset = function() {return 0;}; + +Date.prototype.toDateString = function() {return "";}; +Date.prototype.toGMTString = function() {return "";}; +Date.prototype.toISOString = function() {return "";}; +Date.prototype.toJSON = function() {return "";}; +Date.prototype.toLocaleDateString = function() {return "";}; +Date.prototype.toLocaleFormat = function() {return "";}; +Date.prototype.toLocaleString = function() {return "";}; +Date.prototype.toLocaleTimeString = function() {return "";}; +Date.prototype.toString = function() {return "";}; +Date.prototype.toTimeString = function() {return "";}; +Date.prototype.toUTCString = function() {return "";}; + +Date.prototype.setDate = function() {return 0;}; +Date.prototype.setFullYear = function() {return 0;}; +Date.prototype.setHours = function() {return 0;}; +Date.prototype.setMinutes = function() {return 0;}; +Date.prototype.setMonth = function() {return 0;}; +Date.prototype.setYear = function() {return 0;}; +Date.prototype.setSeconds = function() {return 0;}; +Date.prototype.setMilliseconds = function() {return 0;}; + +Date = function (Date) { + var names = Object.getOwnPropertyNames(Date); + // Loop through them + for (var i = 0; i < names.length; i++) { + // Skip props already in the MyDate object + if (names[i] in MyDate) continue; + // Get property description from o + var desc = Object.getOwnPropertyDescriptor(Date, names[i]); + // Use it to create property on MyDate + Object.defineProperty(MyDate, names[i], desc); + } + + return MyDate; + + function MyDate() { + if (arguments.length >= 0) { + arguments = [0]; + } + return instantiate(Date, arguments); + } +}(Date); +)"; + +static std::string jsLimitMathSet = R"( +var mathKeep = new Set(["abs", "ceil", "floor", "max", "min", "round", "sign", "trunc", "log", "log10", "log2", "pow", + "sqrt", "E", "LN10", "LN2", "LOG10E", "LOG2E", "PI", "SQRT1_2", "SQRT2" ]); +for (var fn of Object.getOwnPropertyNames(Math)) { + if (! mathKeep.has(fn)) { + delete Math[fn]; + } +} +delete Date.now; +)"; + +V8Filter::~V8Filter() +{ + if (m_isRunning) + { + m_engine->GetIsolate()->TerminateExecution(); + } + m_filterFunction.Reset(); + m_context.Reset(); +} + +int V8Filter::Initialize(V8Engine *engine, std::string script, std::string functionName, + std::vector &callback_names, std::string &strResult) +{ + if (fDebug) + LogPrint("v8filter", "v8filter: V8Filter::Initialize\n"); + m_engine = engine; + v8::Isolate *isolate = m_engine->GetIsolate(); + strResult.clear(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + auto global = v8::ObjectTemplate::New(isolate); + auto filterCallback = v8::External::New(isolate, m_engine->GetFilterCallback()); + for (std::string functionName : callback_names) + { + if (callbackLookup.find(functionName) == callbackLookup.end()) + { + strResult = strprintf("Undefined callback name: {}", functionName); + return MC_ERR_INTERNAL_ERROR; + } + global->Set(String2V8(isolate, functionName), + v8::FunctionTemplate::New(isolate, callbackLookup[functionName], filterCallback)); + } + auto context = v8::Context::New(isolate, nullptr, global); + m_context.Reset(isolate, context); + + std::string jsPreamble = jsFixture; + if(mc_gState->m_Features->FixedJSDateFunctions()) + { + jsPreamble=jsFixtureDateFunctions; + } + if (mc_gState->m_Features->FilterLimitedMathSet()) + { + jsPreamble += jsLimitMathSet; + } + + int status = this->CompileAndLoadScript(jsPreamble, "", "preamble", strResult); + if (status != MC_ERR_NOERROR || !strResult.empty()) + { + m_context.Reset(); + return status; + } + status = this->CompileAndLoadScript(script, functionName, "