diff --git a/.github/workflows/komodod_ci.yml b/.github/workflows/komodo_linux_ci.yml similarity index 66% rename from .github/workflows/komodod_ci.yml rename to .github/workflows/komodo_linux_ci.yml index 52810fe8b4b..01cdf457b38 100644 --- a/.github/workflows/komodod_ci.yml +++ b/.github/workflows/komodo_linux_ci.yml @@ -1,24 +1,29 @@ -name: Komodo CI +name: Komodo Linux CI -on: [push, pull_request] +on: + pull_request: + types: [opened, synchronize, reopened] + schedule: + - cron: '0 0 * * 1' jobs: + linux-build: name: Linux Build runs-on: ubuntu-latest - + steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install deps (Linux) + env: DEBIAN_FRONTEND: noninteractive - if: runner.os == 'Linux' run: | - sudo apt-get remove php7.1-fpm php7.2-fpm php7.3-fpm php7.3-common php7.4-fpm msodbcsql17 - sudo apt-get update - sudo apt-get upgrade -y + sudo apt-get update # prevents repo404 errors on apt-remove below + sudo apt-get remove php5.6-fpm php7.0-fpm php7.1-fpm php7.2-fpm php7.3-fpm php7.3-common php7.4-fpm msodbcsql17 mysql-server* sudo apt-get update + sudo ACCEPT_EULA=Y apt-get upgrade -y sudo apt-get install -q \ curl \ python3 \ @@ -30,52 +35,25 @@ jobs: python3 -m pip install setuptools wheel python3 -m pip install slick-bitcoinrpc pytest wget jsonschema ./zcutil/fetch-params.sh - - name: Build (Linux) - if: runner.os == 'Linux' run: | # flag for some CC tests transactions - so DO NOT USE THIS CI ARTIFACTS IN PRODUCTION!!! CONFIGURE_FLAGS='CPPFLAGS=-DTESTMODE' ./zcutil/build.sh -j$(nproc) tar -czvf komodo-linux.tar.gz src/komodod src/komodo-cli - - name: Upload komodo-linux.tar.gz as artifact uses: actions/upload-artifact@v1 with: name: komodo-linux path: ./komodo-linux.tar.gz -# - name: Install deps (macOS) -# if: runner.os == 'macOS' -# run: | -# brew update -# brew upgrade -# brew tap discoteq/discoteq; brew install flock -# brew install autoconf autogen automake -# brew install gcc@8 -# brew install binutils -# brew install protobuf -# brew install coreutils -# brew install wget -# brew install python3 -# pip3 install setuptools wheel slick-bitcoinrpc pytest wget -# ./zcutil/fetch-params.sh - -# - name: Build (macOS) -# if: runner.os == 'macOS' -# run: | -# # flag for some CC tests transactions - so DO NOT USE THIS CI ARTIFACTS IN PRODUCTION!!! -# export CPATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ -# CONFIGURE_FLAGS='CPPFLAGS=-DTESTMODE' ./zcutil/build-mac.sh -j4 -# - name: Run CC tests -# if: runner.os == 'Linux' || runner.os == 'macOS' - linux-test-dice-token-reards-faucet-cc: + name: Test (Linux/Dice, Token, Faucet, Rewards) runs-on: ubuntu-latest needs: linux-build steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install deps (Dice, Token, Faucet, Rewards CC) run: | @@ -90,7 +68,6 @@ jobs: libssl-dev -y python3 -m pip install setuptools wheel python3 -m pip install slick-bitcoinrpc pytest wget jsonschema - - name: Download komodo-linux.tar.gz uses: actions/download-artifact@v1 with: @@ -104,14 +81,14 @@ jobs: ./zcutil/fetch-params.sh cd qa/pytest_komodo ./ci_setup.sh "cc_modules/test_dice.py cc_modules/test_faucet.py cc_modules/test_token.py cc_modules/test_rewards.py" - linux-test-oracles: + name: Test (Linux/OraclesCC) runs-on: ubuntu-latest needs: linux-build steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install deps (OraclesCC) run: | @@ -126,7 +103,6 @@ jobs: libssl-dev -y python3 -m pip install setuptools wheel python3 -m pip install slick-bitcoinrpc pytest wget jsonschema - - name: Download komodo-linux.tar.gz uses: actions/download-artifact@v1 with: @@ -140,14 +116,14 @@ jobs: ./zcutil/fetch-params.sh cd qa/pytest_komodo ./ci_setup.sh cc_modules/test_oracles.py - linux-test-baserpc: + name: Test (Linux/BasicRPC) runs-on: ubuntu-latest needs: linux-build steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install deps (BasicRPC) run: | @@ -162,7 +138,6 @@ jobs: libssl-dev -y python3 -m pip install setuptools wheel python3 -m pip install slick-bitcoinrpc pytest wget jsonschema - - name: Download komodo-linux.tar.gz uses: actions/download-artifact@v1 with: @@ -176,14 +151,14 @@ jobs: ./zcutil/fetch-params.sh cd qa/pytest_komodo ./ci_setup.sh basic - linux-test-channels: + name: Test (Linux/ChannelsCC) runs-on: ubuntu-latest needs: linux-build steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install deps (ChannelsCC) run: | @@ -198,7 +173,6 @@ jobs: libssl-dev -y python3 -m pip install setuptools wheel python3 -m pip install slick-bitcoinrpc pytest wget jsonschema - - name: Download komodo-linux.tar.gz uses: actions/download-artifact@v1 with: @@ -212,14 +186,14 @@ jobs: ./zcutil/fetch-params.sh cd qa/pytest_komodo ./ci_setup.sh cc_modules/test_channels.py - linux-test-heir: + name: Test (Linux/HeirCC) runs-on: ubuntu-latest needs: linux-build steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install deps (HeirCC) run: | @@ -234,7 +208,6 @@ jobs: libssl-dev -y python3 -m pip install setuptools wheel python3 -m pip install slick-bitcoinrpc pytest wget jsonschema - - name: Download komodo-linux.tar.gz uses: actions/download-artifact@v1 with: @@ -248,33 +221,3 @@ jobs: ./zcutil/fetch-params.sh cd qa/pytest_komodo ./ci_setup.sh cc_modules/test_heir.py - - windows-build: - - name: Win Build - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - name: Build Win on Unix - env: - DEBIAN_FRONTEND: noninteractive - if: runner.os == 'Linux' - run: | - sudo apt-get remove php5.6-fpm php7.0-fpm php7.1-fpm php7.2-fpm php7.3-fpm php7.3-common php7.4-fpm msodbcsql17 - sudo apt-get update - sudo ACCEPT_EULA=Y apt-get upgrade -y - sudo apt-get install build-essential pkg-config libc6-dev m4 g++-multilib autoconf libtool libncurses-dev unzip git python zlib1g-dev wget bsdmainutils automake libboost-all-dev libssl-dev libprotobuf-dev protobuf-compiler libqrencode-dev libdb++-dev ntp ntpdate nano software-properties-common curl libevent-dev libcurl4-gnutls-dev cmake clang libsodium-dev -y - sudo apt-get install build-essential pkg-config libc6-dev m4 g++-multilib autoconf libtool ncurses-dev unzip git python python-zmq zlib1g-dev wget libcurl4-gnutls-dev bsdmainutils automake curl cmake mingw-w64 -y - curl https://sh.rustup.rs -sSf | sh -s -- -y - source $HOME/.cargo/env - rustup target add x86_64-pc-windows-gnu - sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix - sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix - CONFIGURE_FLAGS='CPPFLAGS=-DTESTMODE' ./zcutil/build-win.sh -j$(nproc) - zip --junk-paths komodod_win src/komodod.exe - - name: Upload komodod.exe as artifact - uses: actions/upload-artifact@v1 - with: - name: komodod_win - path: ./komodod_win.zip diff --git a/.github/workflows/komodo_mac_ci.yml b/.github/workflows/komodo_mac_ci.yml new file mode 100644 index 00000000000..db950a6e31d --- /dev/null +++ b/.github/workflows/komodo_mac_ci.yml @@ -0,0 +1,185 @@ +name: Komodo MacOS CI + +on: + pull_request: + types: [opened, synchronize, reopened] + schedule: + - cron: '0 0 * * 1' + +jobs: + + macos-build: + name: MacOS Build + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + + - name: Install deps (macOS) + run: | + brew update + brew upgrade + brew tap discoteq/discoteq; brew install flock + brew install autoconf autogen automake + brew install gcc@8 + brew install binutils + brew install protobuf + brew install coreutils + brew install wget + brew install python3 + pip3 install setuptools wheel slick-bitcoinrpc pytest wget + ./zcutil/fetch-params.sh + + - name: Build (macOS) + run: | + # flag for some CC tests transactions - so DO NOT USE THIS CI ARTIFACTS IN PRODUCTION!!! + export CPATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ + ./zcutil/build-mac-dtest.sh -j4 + tar -czvf komodo-macos.tar.gz src/komodod src/komodo-cli + + - name: Upload komodo-macos.tar.gz as artifact + uses: actions/upload-artifact@v1 + with: + name: komodo-macos + path: ./komodo-macos.tar.gz + + macos-test-dice-token-reards-faucet-cc: + + name: Test (MacOS/Dice, Token, Faucet, Rewards) + runs-on: macos-latest + needs: macos-build + + steps: + - uses: actions/checkout@v2 + + - name: Install deps (Dice, Token, Faucet, Rewards CC) + run: | + brew install python3 curl + python3 -m pip install setuptools wheel + python3 -m pip install slick-bitcoinrpc pytest wget jsonschema + + - name: Download komodo-macos.tar.gz + uses: actions/download-artifact@v1 + with: + name: komodo-macos + + - name: Test CC (MacOS) + run: | + mv komodo-macos/komodo-macos.tar.gz . + mkdir -p src + tar xzvf komodo-macos.tar.gz + ./zcutil/fetch-params.sh + cd qa/pytest_komodo + ./ci_setup.sh "cc_modules/test_dice.py cc_modules/test_faucet.py cc_modules/test_token.py cc_modules/test_rewards.py" + + macos-test-oracles: + + name: Test (macos/OraclesCC) + runs-on: macos-latest + needs: macos-build + + steps: + - uses: actions/checkout@v2 + + - name: Install deps (OraclesCC) + run: | + brew install python3 curl + python3 -m pip install setuptools wheel + python3 -m pip install slick-bitcoinrpc pytest wget jsonschema + - name: Download komodo-macos.tar.gz + uses: actions/download-artifact@v1 + with: + name: komodo-macos + + - name: Oracles Test (macos) + run: | + mv komodo-macos/komodo-macos.tar.gz . + mkdir -p src + tar xzvf komodo-macos.tar.gz + ./zcutil/fetch-params.sh + cd qa/pytest_komodo + ./ci_setup.sh cc_modules/test_oracles.py + + macos-test-baserpc: + + name: Test (macos/BasicRPC) + runs-on: macos-latest + needs: macos-build + + steps: + - uses: actions/checkout@v2 + + - name: Install deps (BasicRPC) + run: | + brew install python3 curl + python3 -m pip install setuptools wheel + python3 -m pip install slick-bitcoinrpc pytest wget jsonschema + - name: Download komodo-macos.tar.gz + uses: actions/download-artifact@v1 + with: + name: komodo-macos + + - name: BasicRPC Test (macos) + run: | + mv komodo-macos/komodo-macos.tar.gz . + mkdir -p src + tar xzvf komodo-macos.tar.gz + ./zcutil/fetch-params.sh + cd qa/pytest_komodo + ./ci_setup.sh basic + + macos-test-channels: + + name: Test (macos/ChannelsCC) + runs-on: macos-latest + needs: macos-build + + steps: + - uses: actions/checkout@v2 + + - name: Install deps (ChannelsCC) + run: | + brew install python3 curl + python3 -m pip install setuptools wheel + python3 -m pip install slick-bitcoinrpc pytest wget jsonschema + - name: Download komodo-macos.tar.gz + uses: actions/download-artifact@v1 + with: + name: komodo-macos + + - name: Channels Test (macos) + run: | + mv komodo-macos/komodo-macos.tar.gz . + mkdir -p src + tar xzvf komodo-macos.tar.gz + ./zcutil/fetch-params.sh + cd qa/pytest_komodo + ./ci_setup.sh cc_modules/test_channels.py + + macos-test-heir: + + name: Test (macos/HeirCC) + runs-on: macos-latest + needs: macos-build + + steps: + - uses: actions/checkout@v2 + + - name: Install deps (HeirCC) + run: | + brew install python3 curl + python3 -m pip install setuptools wheel + python3 -m pip install slick-bitcoinrpc pytest wget jsonschema + - name: Download komodo-macos.tar.gz + uses: actions/download-artifact@v1 + with: + name: komodo-macos + + - name: Heir Test (macos) + run: | + mv komodo-macos/komodo-macos.tar.gz . + mkdir -p src + tar xzvf komodo-macos.tar.gz + ./zcutil/fetch-params.sh + cd qa/pytest_komodo + ./ci_setup.sh cc_modules/test_heir.py diff --git a/.github/workflows/komodo_win_ci.yml b/.github/workflows/komodo_win_ci.yml new file mode 100644 index 00000000000..a0b89da35cb --- /dev/null +++ b/.github/workflows/komodo_win_ci.yml @@ -0,0 +1,224 @@ +name: Komodo Win CI + +on: + pull_request: + types: [opened, synchronize, reopened] + schedule: + - cron: '0 0 * * 1' + +jobs: + + windows-build: + + name: Win Build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Install deps(mingw) + env: + DEBIAN_FRONTEND: noninteractive + run: | + sudo apt-get update # prevents repo404 errors on apt-remove below + sudo apt-get remove php5.6-fpm php7.0-fpm php7.1-fpm php7.2-fpm php7.3-fpm php7.3-common php7.4-fpm msodbcsql17 + sudo ACCEPT_EULA=Y apt-get upgrade -y + sudo apt-get update + sudo apt-get install build-essential \ + pkg-config \ + libc6-dev \ + m4 \ + g++-multilib \ + autoconf libtool \ + libncurses-dev \ + unzip \ + git \ + python \ + zlib1g-dev \ + wget \ + bsdmainutils \ + automake \ + libboost-all-dev \ + libssl-dev \ + libprotobuf-dev \ + protobuf-compiler \ + libqrencode-dev \ + libdb++-dev \ + ntp \ + ntpdate \ + nano \ + software-properties-common \ + curl \ + libevent-dev \ + libcurl4-gnutls-dev \ + cmake \ + clang \ + libsodium-dev \ + python-zmq \ + mingw-w64 -y + curl https://sh.rustup.rs -sSf | sh -s -- -y + source $HOME/.cargo/env + rustup target add x86_64-pc-windows-gnu + sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix + sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix + - name: Build Win (mingw) + run: | + # flag for some CC tests transactions - so DO NOT USE THIS CI ARTIFACTS IN PRODUCTION!!! + export CONFIGURE_FLAGS='CPPFLAGS=-DTESTMODE' + ./zcutil/build-win-dtest.sh -j$(nproc) + zip --junk-paths komodod_win src/komodod.exe src/komodo-cli.exe + - name: Upload komodod.exe as artifact + uses: actions/upload-artifact@v1 + with: + name: komodod_win + path: ./komodod_win.zip + + windows-test-baserpc: + + name: Test (Win/BasicRPC) + needs: windows-build + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + + - name: Download komodo_win.zip + uses: actions/download-artifact@v1 + with: + name: komodod_win + + - name: Install deps (Base) + shell: cmd + run: | + move komodod_win\komodod_win.zip + 7z e komodod_win.zip + move komodod.exe src\ + python.exe -m pip install --upgrade setuptools + python.exe -m pip install --upgrade pip + python.exe -m pip install pycurl pytest wget jsonschema + python.exe -m pip install slick-bitcoinrpc + zcutil\fetch-params.bat + - name: Base RPC Test (Windows) + shell: cmd + run: | + cd qa\pytest_komodo + start_ci.bat basic + windows-test-dice-faucet-tok-rewCC: + + name: Test (Win/Dice Faucet Token Rewards) + runs-on: windows-latest + needs: windows-build + + steps: + - uses: actions/checkout@v2 + + - name: Download komodo_win.zip + uses: actions/download-artifact@v1 + with: + name: komodod_win + + - name: Install deps (MainCC) + shell: cmd + run: | + move komodod_win\komodod_win.zip + 7z e komodod_win.zip + move komodod.exe src\ + python.exe -m pip install --upgrade setuptools + python.exe -m pip install --upgrade pip + python.exe -m pip install pycurl pytest wget jsonschema + python.exe -m pip install slick-bitcoinrpc + zcutil\fetch-params.bat + - name: CC Dice Faucet Tokens Rewards (Windows) + shell: cmd + run: | + cd qa\pytest_komodo + start_ci.bat cc_modules\test_dice.py cc_modules\test_faucet.py cc_modules\test_token.py cc_modules\test_rewards.py + windows-test-oracles-cc: + + name: Test (Win/OraclesCC) + runs-on: windows-latest + needs: windows-build + + steps: + - uses: actions/checkout@v2 + + - name: Download komodo_win.zip + uses: actions/download-artifact@v1 + with: + name: komodod_win + + - name: Install deps (OraclesCC) + shell: cmd + run: | + move komodod_win\komodod_win.zip + 7z e komodod_win.zip + move komodod.exe src\ + python.exe -m pip install --upgrade setuptools + python.exe -m pip install --upgrade pip + python.exe -m pip install pycurl pytest wget jsonschema + python.exe -m pip install slick-bitcoinrpc + zcutil\fetch-params.bat + - name: CC Oracles (Windows) + shell: cmd + run: | + cd qa\pytest_komodo + start_ci.bat cc_modules\test_oracles.py + windows-test-heir-cc: + + name: Test (Win/HeirCC) + runs-on: windows-latest + needs: windows-build + + steps: + - uses: actions/checkout@v2 + + - name: Download komodo_win.zip + uses: actions/download-artifact@v1 + with: + name: komodod_win + + - name: Install deps (CC Heir) + shell: cmd + run: | + move komodod_win\komodod_win.zip + 7z e komodod_win.zip + move komodod.exe src\ + python.exe -m pip install --upgrade setuptools + python.exe -m pip install --upgrade pip + python.exe -m pip install pycurl pytest wget jsonschema + python.exe -m pip install slick-bitcoinrpc + zcutil\fetch-params.bat + - name: CC Heir (Windows) + shell: cmd + run: | + cd qa\pytest_komodo + start_ci.bat cc_modules\test_heir.py + windows-test-channels-cc: + + name: Test (Win/ChannelsCC) + runs-on: windows-latest + needs: windows-build + + steps: + - uses: actions/checkout@v2 + + - name: Download komodo_win.zip + uses: actions/download-artifact@v1 + with: + name: komodod_win + + - name: Install deps (ChannelsCC) + shell: cmd + run: | + move komodod_win\komodod_win.zip + 7z e komodod_win.zip + move komodod.exe src\ + python.exe -m pip install --upgrade setuptools + python.exe -m pip install --upgrade pip + python.exe -m pip install pycurl pytest wget jsonschema + python.exe -m pip install slick-bitcoinrpc + zcutil\fetch-params.bat + - name: ChannelsCC (Windows) + shell: cmd + run: | + cd qa\pytest_komodo + start_ci.bat cc_modules\test_channels.py diff --git a/.github/workflows/komodod_cd.yml b/.github/workflows/komodod_cd.yml index 90aa5736cac..726465e0cfe 100644 --- a/.github/workflows/komodod_cd.yml +++ b/.github/workflows/komodod_cd.yml @@ -34,10 +34,10 @@ jobs: - name: Install deps (Linux) run: | - sudo apt-get remove php7.1-fpm php7.2-fpm php7.3-fpm php7.3-common php7.4-fpm msodbcsql17 - sudo apt-get update - sudo apt-get upgrade -y + sudo apt-get update # prevents repo404 errors on apt-remove below + sudo apt-get remove php5.6-fpm php7.0-fpm php7.1-fpm php7.2-fpm php7.3-fpm php7.3-common php7.4-fpm msodbcsql17 mysql-server* sudo apt-get update + sudo ACCEPT_EULA=Y apt-get upgrade -y sudo apt-get install -q \ curl \ python3 \ @@ -108,6 +108,7 @@ jobs: env: DEBIAN_FRONTEND: noninteractive run: | + sudo apt-get update # prevents repo404 errors on apt-remove below sudo apt-get remove php5.6-fpm php7.0-fpm php7.1-fpm php7.2-fpm php7.3-fpm php7.3-common php7.4-fpm sudo apt-get update sudo apt-get upgrade -y diff --git a/COPYING b/COPYING index 587a47035fc..8d4ed74e0da 100644 --- a/COPYING +++ b/COPYING @@ -1,7 +1,7 @@ Copyright (c) 2009-2017 The Bitcoin Core developers Copyright (c) 2009-2018 Bitcoin Developers Copyright (c) 2016-2017 The Zcash developers -Copyright (c) 2016-2019 The Komodo developers +Copyright (c) 2016-2020 The Komodo developers Copyright (c) 2018 The VerusCoin developers Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/README.md b/README.md index f10ffc6fd30..c8f99f2e16f 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,12 @@ sudo apt-get install build-essential pkg-config libc6-dev m4 g++-multilib autoco curl https://sh.rustup.rs -sSf | sh source $HOME/.cargo/env rustup target add x86_64-pc-windows-gnu + +sudo update-alternatives --config x86_64-w64-mingw32-gcc +# (configure to use POSIX variant) +sudo update-alternatives --config x86_64-w64-mingw32-g++ +# (configure to use POSIX variant) + git clone https://github.com/jl777/komodo --branch master --single-branch cd komodo ./zcutil/fetch-params.sh diff --git a/depends/funcs.mk b/depends/funcs.mk index 3d89de8a703..11c5843ec5d 100644 --- a/depends/funcs.mk +++ b/depends/funcs.mk @@ -91,8 +91,9 @@ $(1)_download_path_fixed=$(subst :,\:,$$($(1)_download_path)) #default commands +# The default behavior for tar will try to set ownership when running as uid 0 and may not succeed, --no-same-owner disables this behavior $(1)_fetch_cmds ?= $(call fetch_file,$(1),$(subst \:,:,$$($(1)_download_path_fixed)),$$($(1)_download_file),$($(1)_file_name),$($(1)_sha256_hash)) -$(1)_extract_cmds ?= mkdir -p $$($(1)_extract_dir) && echo "$$($(1)_sha256_hash) $$($(1)_source)" > $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_SHA256SUM) -c $$($(1)_extract_dir)/.$$($(1)_file_name).hash && tar --strip-components=1 -xf $$($(1)_source) +$(1)_extract_cmds ?= mkdir -p $$($(1)_extract_dir) && echo "$$($(1)_sha256_hash) $$($(1)_source)" > $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_SHA256SUM) -c $$($(1)_extract_dir)/.$$($(1)_file_name).hash && tar --no-same-owner --strip-components=1 -xf $$($(1)_source) $(1)_preprocess_cmds ?= $(1)_build_cmds ?= $(1)_config_cmds ?= @@ -193,7 +194,7 @@ $($(1)_preprocessed): | $($(1)_dependencies) $($(1)_extracted) $(AT)touch $$@ $($(1)_configured): | $($(1)_preprocessed) $(AT)echo Configuring $(1)... - $(AT)rm -rf $(host_prefix); mkdir -p $(host_prefix)/lib; cd $(host_prefix); $(foreach package,$($(1)_all_dependencies), tar xf $($(package)_cached); ) + $(AT)rm -rf $(host_prefix); mkdir -p $(host_prefix)/lib; cd $(host_prefix); $(foreach package,$($(1)_all_dependencies), tar --no-same-owner -xf $($(package)_cached); ) $(AT)mkdir -p $$(@D) $(AT)+cd $$(@D); $($(1)_config_env) $(call $(1)_config_cmds, $(1)) $(AT)touch $$@ diff --git a/qa/pytest_komodo/basic/pytest_util.py b/qa/pytest_komodo/basic/pytest_util.py index a4a31e59bd2..ab1e395fa26 100644 --- a/qa/pytest_komodo/basic/pytest_util.py +++ b/qa/pytest_komodo/basic/pytest_util.py @@ -4,11 +4,12 @@ import random import string import hashlib +import re try: from slickrpc import Proxy from slickrpc.exc import RpcException as RPCError from pycurl import error as HttpError -except ImportError: +except ImportError: # fallback to bitcoinrpc from bitcoinrpc.authproxy import AuthServiceProxy as Proxy from bitcoinrpc.authproxy import JSONRPCException as RPCError from http.client import HTTPException as HttpError @@ -35,7 +36,7 @@ def validate_proxy(env_params_dictionary, proxy, node=0): except Exception as e: print("Coennction failed, error: ", e, "\nRetrying") attempts += 1 - time.sleep(2) + time.sleep(10) if attempts > 15: raise ChildProcessError("Node ", node, " does not respond") print("IMPORTING PRIVKEYS") @@ -72,13 +73,18 @@ def enable_mining(proxy): raise ChildProcessError("Node did not start correctly, aborting\n") -def mine_and_waitconfirms(txid, proxy): # should be used after tx is send +def mine_and_waitconfirms(txid, proxy, confs_req=2): # should be used after tx is send # we need the tx above to be confirmed in the next block attempts = 0 while True: try: confirmations_amount = proxy.getrawtransaction(txid, 1)['confirmations'] - break + if confirmations_amount < confs_req: + print("\ntx is not confirmed yet! Let's wait a little more") + time.sleep(5) + else: + print("\ntx confirmed") + return True except KeyError as e: print("\ntx is in mempool still probably, let's wait a little bit more\nError: ", e) time.sleep(5) @@ -88,13 +94,6 @@ def mine_and_waitconfirms(txid, proxy): # should be used after tx is send else: print("\nwaited too long - probably tx stuck by some reason") return False - if confirmations_amount < 2: - print("\ntx is not confirmed yet! Let's wait a little more") - time.sleep(5) - return True - else: - print("\ntx confirmed") - return True def validate_transaction(proxy, txid, conf_req): @@ -201,7 +200,7 @@ def randomhex(): # returns 64 chars long pubkey-like hex string return (''.join(random.choice(chars) for i in range(64))).lower() -def write_file(filename): +def write_file(filename): # creates text file lines = 10 content = '' for x in range(lines): @@ -212,6 +211,14 @@ def write_file(filename): return True +def write_empty_file(filename: str, size: int): # creates empty file slightly bigger than size in mb + if os.path.isfile(filename): + os.remove(filename) + with open(filename, 'wb') as f: + f.seek((size * 1024 * 1025) - 1) + f.write(b'\0') + + def get_size(file): if os.path.isfile(file): return os.path.getsize(file) @@ -227,3 +234,35 @@ def get_filehash(file): return str(fhash) else: raise FileNotFoundError + + +def validate_tx_pattern(txid): + if not isinstance(txid, str): + return False + pattern = re.compile('[0-9a-f]{64}') + if pattern.match(txid): + return True + else: + return False + + +def validate_raddr_pattern(addr): + if not isinstance(addr, str): + return False + address_pattern = re.compile(r"R[a-zA-Z0-9]{33}\Z") + if address_pattern.match(addr): + return True + else: + return False + + +def wait_blocks(rpc_connection, blocks_to_wait): + init_height = int(rpc_connection.getinfo()["blocks"]) + while True: + current_height = int(rpc_connection.getinfo()["blocks"]) + height_difference = current_height - init_height + if height_difference < blocks_to_wait: + print("Waiting for more blocks") + time.sleep(5) + else: + return True \ No newline at end of file diff --git a/qa/pytest_komodo/basic/test_rawtransactions.py b/qa/pytest_komodo/basic/test_rawtransactions.py index f07dab51af2..e5d11ffc63f 100644 --- a/qa/pytest_komodo/basic/test_rawtransactions.py +++ b/qa/pytest_komodo/basic/test_rawtransactions.py @@ -32,13 +32,12 @@ def test_rawtransactions(self, test_params): # create, fund, sign, send calls txid = res[0].get('txid') vout = res[0].get('vout') base_amount = res[0].get('amount') - # python float() is double precision floating point number, - # createrawtransaction method expects C float (8 digits) value - # "{0:.8f}".format(value)) returns number string with 8 digit precision and float() corrects the type - if isinstance(base_amount, Decimal): - amount = float("{0:.8f}".format(float(base_amount) * 0.9)) + # "{0:.8f}".format(value)) returns number string with 8 digit precision + correction = Decimal(0.9) + if isinstance(base_amount, Decimal): # can be float or Decimal depending on proxy used + amount = "{0:.8f}".format(base_amount * correction) else: - amount = float("{0:.8f}".format(base_amount * 0.9)) + amount = "{0:.8f}".format(Decimal(base_amount) * correction) address = rpc.getnewaddress() ins = [{'txid': txid, 'vout': vout}] outs = {address: amount} diff --git a/qa/pytest_komodo/basic/test_shielded.py b/qa/pytest_komodo/basic/test_shielded.py index d943eee8405..c336cf4ebdd 100644 --- a/qa/pytest_komodo/basic/test_shielded.py +++ b/qa/pytest_komodo/basic/test_shielded.py @@ -52,7 +52,7 @@ def test_z_send(self, test_params): 'type': 'object', 'properties': { 'address': {'type': 'string'}, - 'amount': {'type': ['integer', 'number']} + 'amount': {'type': 'string'} } } } @@ -84,23 +84,13 @@ def test_z_send(self, test_params): shielded1 = rpc1.z_getnewaddress() transparent2 = rpc2.getnewaddress() shielded2 = rpc2.z_getnewaddress() - amount1 = float("{0:.8f}".format(rpc1.listunspent()[-1].get('amount') / 10)) - amount2 = float("{0:.8f}".format(amount1 / 10)) - try: - import slickrpc - authproxy = 0 - except ImportError: - authproxy = 1 - if authproxy: # type correction when using python-bitcoinrpc Proxy - amount1 = float(amount1) - amount2 = float(amount2) - # python float() is double precision floating point number, - # where z_sendmany expects regural float (8 digits) value - # "{0:.8f}".format(value)) returns number string with 8 digit precision and float() corrects the type - t_send1 = [{'address': transparent1, 'amount': float("{0:.8f}".format(amount2))}] - t_send2 = [{'address': transparent2, 'amount': float("{0:.8f}".format(amount2 * 0.4))}] - z_send1 = [{'address': shielded1, 'amount': float("{0:.8f}".format(amount2 * 0.95))}] - z_send2 = [{'address': shielded2, 'amount': float("{0:.8f}".format(amount2 * 0.4))}] + amount1 = Decimal("{0:.8f}".format(rpc1.listunspent()[-1].get('amount') / 10)) + amount2 = Decimal("{0:.8f}".format(amount1 / 10)) + # "{0:.8f}".format(value)) returns number string with 8 digit precision + t_send1 = [{'address': transparent1, 'amount': ("{0:.8f}".format(amount2))}] + t_send2 = [{'address': transparent2, 'amount': "{0:.8f}".format(amount2 * Decimal(0.4))}] + z_send1 = [{'address': shielded1, 'amount': "{0:.8f}".format(amount2 * Decimal(0.95))}] + z_send2 = [{'address': shielded2, 'amount': "{0:.8f}".format(amount2 * Decimal(0.4))}] cases = [(transparent1, t_send1), (transparent1, z_send1), (shielded1, t_send2), (shielded1, z_send2)] # sendmany cannot use coinbase tx vouts txid = rpc1.sendtoaddress(transparent1, amount1) diff --git a/qa/pytest_komodo/basic/test_wallet.py b/qa/pytest_komodo/basic/test_wallet.py index 2da614cdb2d..87acd29bd15 100644 --- a/qa/pytest_komodo/basic/test_wallet.py +++ b/qa/pytest_komodo/basic/test_wallet.py @@ -4,8 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. import pytest -import os -import time from decimal import * from pytest_util import validate_template, mine_and_waitconfirms @@ -306,10 +304,8 @@ def test_signmessage(self, test_params): def test_sendtoaddress(self, test_params): rpc = test_params.get('node1').get('rpc') addr = rpc.getnewaddress() - # python float() is double precision floating point number, - # where sendmany expects regural float (8 digits) value - # "{0:.8f}".format(value)) returns number string with 8 digit precision and float() corrects the type - amount = float("{0:.8f}".format(rpc.listunspent()[-1].get('amount') / 10)) + # "{0:.8f}".format(value)) returns number string with 8 digit precision + amount = ("{0:.8f}".format(Decimal(rpc.listunspent()[-1].get('amount')) / Decimal(10))) txid = rpc.sendtoaddress(addr, amount) assert isinstance(txid, str) # wait tx to be confirmed @@ -320,8 +316,7 @@ def test_sendmany(self, test_params): rpc2 = test_params.get('node2').get('rpc') address1 = rpc1.getnewaddress() address2 = rpc2.getnewaddress() - # clarification in test_sendtoaddress above - amount = float("{0:.8f}".format(rpc1.listunspent()[-1].get('amount') / 10)) # float("{0:.8f}".format(amount2)) + amount = ("{0:.8f}".format(Decimal(rpc1.listunspent()[-1].get('amount')) / Decimal(10))) send = {address1: amount, address2: amount} txid = rpc1.sendmany("", send) assert isinstance(txid, str) diff --git a/qa/pytest_komodo/chainconfig.json b/qa/pytest_komodo/chainconfig.json index 32775a55379..47f376e425f 100644 --- a/qa/pytest_komodo/chainconfig.json +++ b/qa/pytest_komodo/chainconfig.json @@ -1,14 +1,57 @@ { "TONYCI": { - "coin": "TONYCI", "rpc_user": "test", "rpcpassword": "test", "rpcallowip": "0.0.0.0/0", "rpcport": 7000, "port": 6000, "rpcbind": "0.0.0.0", + "ac_name": "TONYCI", "ac_reward": "100000000000", "ac_supply": "10000000000", "ac_cc": "2" + }, + "HUSH3": { + "rpc_user": "test", + "rpcpassword": "test", + "rpcallowip": "0.0.0.0/0", + "rpcport": 7000, + "rpcbind": "0.0.0.0", + "ac_name": "HUSH3", + "ac_sapling": "1", + "ac_reward": "0,1125000000,562500000", + "ac_halving": "129,340000,840000", + "ac_end": "128,340000,5422111", + "ac_eras": "3", + "ac_blocktime": "150", + "ac_cc": "2", + "ac_ccenable": "228,234,235,236,241", + "ac_founders": "1", + "ac_supply": "6178674", + "ac_perc": "11111111", + "clientname": "GoldenSandtrout", + "addnode": "188.165.212.101", + "ac_cclib": "hush3", + "ac_script": "76a9145eb10cf64f2bab1b457f1f25e658526155928fac88ac" + }, + "KMD": { + "rpc_user": "test", + "rpcpassword": "test", + "rpcallowip": "0.0.0.0/0", + "rpcport": 7000, + "rpcbind": "0.0.0.0" + }, + "PIRATE": { + "rpc_user": "test", + "rpcpassword": "test", + "rpcallowip": "0.0.0.0/0", + "rpcport": 7000, + "rpcbind": "0.0.0.0", + "ac_name": "PIRATE", + "ac_supply": "0", + "ac_reward": "25600000000", + "ac_halving": "77777", + "ac_private": "1", + "addnode": "178.63.77.56" } } \ No newline at end of file diff --git a/qa/pytest_komodo/chainstart.py b/qa/pytest_komodo/chainstart.py index dc71d59fa6b..eeebb6cf350 100644 --- a/qa/pytest_komodo/chainstart.py +++ b/qa/pytest_komodo/chainstart.py @@ -63,34 +63,48 @@ def load_ac_params(asset, chain_mode='default'): return ac -# TODO: add coins file compatibility with create_configs func def create_configs(asset, node=0): if os.name == 'posix': confpath = ('./node_' + str(node) + '/' + asset + '.conf') else: confpath = (os.getcwd() + '\\node_' + str(node) + '\\' + asset + '.conf') - if not os.path.isfile(confpath): - os.mkdir('node_' + str(node)) - open(confpath, 'a').close() - with open(confpath, 'a') as conf: - conf.write("rpcuser=test\n") - conf.write("rpcpassword=test\n") - conf.write('rpcport=' + str(7000 + node) + '\n') - conf.write("rpcbind=0.0.0.0\n") - conf.write("rpcallowip=0.0.0.0/0\n") + if os.path.isfile(confpath) or os.path.isdir(os.getcwd() + '/node_' + str(node)): + for root, dirs, files in os.walk('node_' + str(node), topdown=False): + for name in files: + os.remove(os.path.join(root, name)) + for name in dirs: + os.rmdir(os.path.join(root, name)) + os.rmdir('node_' + str(node)) + print("Clean up done") + os.mkdir('node_' + str(node)) + open(confpath, 'a').close() + with open(confpath, 'a') as conf: + conf.write("rpcuser=test\n") + conf.write("rpcpassword=test\n") + conf.write('rpcport=' + str(7000 + node) + '\n') + conf.write("rpcbind=0.0.0.0\n") + conf.write("rpcallowip=0.0.0.0/0\n") def main(): env_params = load_env_config() clients_to_start = env_params.get('clients_to_start') aschain = env_params.get('ac_name') - for node in range(clients_to_start): # prepare config folders - create_configs(aschain, node) + if os.path.isfile('bootstrap.tar.gz'): + os.remove('bootstrap.tar.gz') + pass if env_params.get('is_bootstrap_needed'): # bootstrap chains - if not os.path.isfile('bootstrap.tar.gz'): - wget.download(env_params.get('bootstrap_url'), "bootstrap.tar.gz") + print("Downloading bootstrap") + wget.download(env_params.get('bootstrap_url'), "bootstrap.tar.gz") + try: tf = tarfile.open("bootstrap.tar.gz") - for i in range(clients_to_start): + btrp = True + except FileNotFoundError: + tf = "" + btrp = False + for i in range(clients_to_start): + create_configs(aschain, i) + if btrp: tf.extractall("node_" + str(i)) mode = env_params.get('chain_start_mode') ac_params = load_ac_params(aschain, mode) @@ -102,22 +116,27 @@ def main(): confpath = (os.getcwd() + '\\node_' + str(i) + '\\' + aschain + '.conf') datapath = (os.getcwd() + '\\node_' + str(i)) cl_args = [ac_params.get('binary_path'), - '-ac_name=' + aschain, '-conf=' + confpath, - '-datadir=' + datapath, - '-pubkey=' + env_params.get('test_pubkey')[i], + '-datadir=' + datapath ] + try: + pubkey = env_params.get('test_pubkey')[i] + cl_args.append('-pubkey=' + pubkey) + except IndexError: + pass if i == 0: for key in ac_params.keys(): - cl_args.append('-' + key + '=' + str(ac_params.get(key))) + if key not in ['binary_path', 'daemon_params', 'rpc_user', 'rpcpassword'] and ac_params.get(key): + cl_args.append('-' + key + '=' + str(ac_params.get(key))) else: cl_args.append('-addnode=127.0.0.1:' + str(ac_params.get('port'))) for key in ac_params.keys(): - if isinstance(ac_params.get(key), int): - data = ac_params.get(key) + 1 - cl_args.append('-' + key + '=' + str(data)) - else: - cl_args.append('-' + key + '=' + str(ac_params.get(key))) + if key not in ['binary_path', 'daemon_params', 'rpc_user', 'rpcpassword'] and ac_params.get(key): + if isinstance(ac_params.get(key), int): + data = ac_params.get(key) + i + cl_args.append('-' + key + '=' + str(data)) + else: + cl_args.append('-' + key + '=' + str(ac_params.get(key))) cl_args.extend(ac_params.get('daemon_params')) print(cl_args) if os.name == "posix": @@ -133,8 +152,8 @@ def main(): 'rpc_port': 7000 + i } rpc_p = create_proxy(node_params) - enable_mining(rpc_p) validate_proxy(env_params, rpc_p, i) + enable_mining(rpc_p) if __name__ == '__main__': diff --git a/qa/pytest_komodo/ci_set_dex.sh b/qa/pytest_komodo/ci_set_dex.sh index 1e2ff331f66..644d5025c18 100755 --- a/qa/pytest_komodo/ci_set_dex.sh +++ b/qa/pytest_komodo/ci_set_dex.sh @@ -20,4 +20,4 @@ export BOOTSTRAP_URL="https://sirseven.me/share/bootstrap.tar.gz" python3 chainstart.py # starting the tests -python3 -m pytest $@ -s -vv modules/test_dexp2p.py modules/test_dexp2p_e2e.py +python3 -m pytest -s -vv modules/test_dexp2p.py modules/test_dexp2p_e2e.py \ No newline at end of file diff --git a/qa/pytest_komodo/ci_sync_chain.sh b/qa/pytest_komodo/ci_sync_chain.sh new file mode 100755 index 00000000000..c997e215b21 --- /dev/null +++ b/qa/pytest_komodo/ci_sync_chain.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Usage: ./ci_sync_chain.sh "CHAIN" "BOOL_BOOTSTRAP_NEEDED" "BOOTSTRAP_LINK" +# example: ./ci_sync_chain.sh "KMD" "False" "" +# chains start script params +export CLIENTS=1 +export CHAIN=$1 +export TEST_ADDY0="RPWhA4f4ZTZxNi5K36bcwsWdVjSVDSjUnd" +export TEST_WIF0="UpcQympViQpLmv1WzMwszKPrmKUa28zsv8pdLCMgNMXDFBBBKxCN" +export TEST_PUBKEY0="02f0ec2d3da51b09e4fc8d9ba334c275b02b3ab6f22ce7be0ea5059cbccbd1b8c7" +export CHAIN_MODE="REGULAR" +export IS_BOOTSTRAP_NEEDED=$2 +export BOOTSTRAP_URL=$3 +export NOTARIZATIONS="True" +export BLOCKTIME_AVR=60 + +# starting the chains +python3 chainstart.py + +# starting the tests +python3 -m pytest sync/test_sync.py -s -vv diff --git a/qa/pytest_komodo/conftest.py b/qa/pytest_komodo/conftest.py index f608c837318..a1392e78612 100644 --- a/qa/pytest_komodo/conftest.py +++ b/qa/pytest_komodo/conftest.py @@ -3,9 +3,9 @@ import os import time # Using different proxy to bypass libcurl issues on Windows -if os.name == 'posix': +try: from slickrpc import Proxy -else: +except ImportError: from bitcoinrpc.authproxy import AuthServiceProxy as Proxy @@ -18,9 +18,9 @@ def _proxy_connection(node_params_dictionary): proxy = Proxy("http://%s:%s@%s:%d" % (node_params_dictionary["rpc_user"], node_params_dictionary["rpc_password"], node_params_dictionary["rpc_ip"], - node_params_dictionary["rpc_port"]), timeout=120) + node_params_dictionary["rpc_port"]), timeout=360) proxy_connected.append(proxy) - except (ConnectionAbortedError, pycurl.error) as e: + except ConnectionAbortedError as e: raise Exception("Connection error! Probably no daemon on selected port. Error: ", e) return proxy @@ -31,7 +31,7 @@ def _proxy_connection(node_params_dictionary): time.sleep(10) # time wait for tests to finish correctly before stopping daemon try: # while using AuthServiceProxy, stop method results in connection aborted error each.stop() - except (ConnectionAbortedError, pycurl.error) as e: + except ConnectionAbortedError as e: print(e) @@ -54,4 +54,4 @@ def test_params(proxy_connection): rpc = proxy_connection(node_params) test_params.update({node: node_params}) test_params[node].update({'rpc': rpc}) - return test_params + return test_params \ No newline at end of file diff --git a/qa/pytest_komodo/modules/test_dexp2p.py b/qa/pytest_komodo/modules/test_dexp2p.py index e568f92fdb0..3645f9167ee 100644 --- a/qa/pytest_komodo/modules/test_dexp2p.py +++ b/qa/pytest_komodo/modules/test_dexp2p.py @@ -6,25 +6,34 @@ import pytest # from decimal import * import sys +import os +import time sys.path.append('../') -from basic.pytest_util import validate_template, randomhex, write_file +from basic.pytest_util import validate_template, randomhex, write_file, write_empty_file @pytest.mark.usefixtures("proxy_connection") class TestDexP2Prpc: def test_dexrpc_stats(self, test_params): - rpc1 = test_params.get('node1').get('rpc') - schema_stats = { 'type': 'object', 'properties': { 'publishable_pubkey': {'type': 'string'}, - 'perfstats': {'type': 'string'} + 'perfstats': {'type': 'string'}, + 'result': {'type': 'string'}, + 'secpkey': {'type': 'string'}, + 'recvaddr': {'type': 'string'}, + 'recvZaddr': {'type': 'string'}, + 'handle': {'type': 'string'}, + 'txpowbits': {'type': 'integer'}, + 'vip': {'type': 'integer'}, + 'cmdpriority': {'type': 'integer'} } } + rpc1 = test_params.get('node1').get('rpc') res = rpc1.DEX_stats() validate_template(res, schema_stats) @@ -332,9 +341,16 @@ def test_dexrpc_setpubkey(self, test_params): schema_setpub = { 'type': 'object', 'properties': { - 'result': {'type': 'string'}, 'publishable_pubkey': {'type': 'string'}, 'perfstats': {'type': 'string'}, + 'result': {'type': 'string'}, + 'secpkey': {'type': 'string'}, + 'recvaddr': {'type': 'string'}, + 'recvZaddr': {'type': 'string'}, + 'handle': {'type': 'string'}, + 'txpowbits': {'type': 'integer'}, + 'vip': {'type': 'integer'}, + 'cmdpriority': {'type': 'integer'} } } @@ -342,3 +358,61 @@ def test_dexrpc_setpubkey(self, test_params): res = rpc1.DEX_setpubkey('01' + randomhex()) validate_template(res, schema_setpub) assert res.get('result') == 'success' + + def test_dexrpc_stream(self, test_params): + # stream file, single iteration + schema_stream = { + 'type': 'object', + 'properties': { + 'fname': {'type': 'string'}, + 'senderpub': {'type': 'string'}, + 'filehash': {'type': 'string'}, + 'checkhash': {'type': 'string'}, + 'result': {'type': 'string'}, + 'id': {'type': 'integer'}, + 'filesize': {'type': 'integer'}, + 'fragments': {'type': 'integer'}, + 'numlocators': {'type': 'integer'} + } + } + + rpc1 = test_params.get('node1').get('rpc') + filename = 'file_to_stream' + write_empty_file(filename, 1) + res = rpc1.DEX_stream(filename, '6') + validate_template(res, schema_stream) + assert res.get('result') == 'success' + if os.path.isfile(filename): + os.remove(filename) + + def test_dexrpc_streamsub(self, test_params): + # subscribe to streamed file, single iteration + schema_streamsub = { + 'type': 'object', + 'properties': { + 'fname': {'type': 'string'}, + 'senderpub': {'type': 'string'}, + 'filehash': {'type': 'string'}, + 'checkhash': {'type': 'string'}, + 'result': {'type': 'string'}, + 'id': {'type': 'integer'}, + 'filesize': {'type': 'integer'}, + 'fragments': {'type': 'integer'}, + 'numlocators': {'type': 'integer'} + } + } + + rpc1 = test_params.get('node1').get('rpc') + rpc2 = test_params.get('node2').get('rpc') + filename = 'file_to_stream' + pubkey = rpc1.DEX_stats().get('publishable_pubkey') + write_empty_file(filename, 1) + res = rpc1.DEX_stream(filename, '6') + assert res.get('result') == 'success' + time.sleep(15) # time to broadcast + res = rpc2.DEX_streamsub(filename, '0', pubkey) + assert res.get('result') == 'error' # will always fail with current test setup + print(res) + validate_template(res, schema_streamsub) + if os.path.isfile(filename): + os.remove(filename) \ No newline at end of file diff --git a/qa/pytest_komodo/modules/test_dexp2p_e2e.py b/qa/pytest_komodo/modules/test_dexp2p_e2e.py index c08ba231bb1..062a86edb9e 100644 --- a/qa/pytest_komodo/modules/test_dexp2p_e2e.py +++ b/qa/pytest_komodo/modules/test_dexp2p_e2e.py @@ -7,10 +7,11 @@ import time # from decimal import * import sys +import os sys.path.append('../') -from basic.pytest_util import randomstring, in_99_range, collect_orderids,\ - get_size, write_file, get_filehash, compare_rough +from basic.pytest_util import randomstring, in_99_range, collect_orderids, check_synced, \ + get_size, write_file, get_filehash, compare_rough, write_empty_file @pytest.mark.usefixtures("proxy_connection") @@ -308,6 +309,19 @@ def test_file_publish(self, test_params): fhash1 = get_filehash(filename1) fhash2 = get_filehash(filename2) + # check error filename_too_long + filename_err = 'file_' + randomstring(15) + res = rpc1.DEX_publish(filename_err, '0') + assert res.get('result') == 'error' + assert res.get('error') == "filename longer than 15 chars" + + # check error file not exist + filename_err = randomstring(1) + res = rpc1.DEX_publish(filename_err, '0') + assert res.get('result') == 'error' + assert res.get('error') == "file not found" + assert res.get('filename') == filename_err + # publish both files on 1st node res = rpc1.DEX_publish(filename1, '0') f_id1 = str(res.get('id')) @@ -322,6 +336,7 @@ def test_file_publish(self, test_params): assert res.get('filesize') == size2 assert res.get('filehash') == fhash2 time.sleep(20) # time to broadcast files to node1 + check_synced(rpc1, rpc2) # Both nodes should be able locate file by files tag and locators tag res = rpc1.DEX_list('0', '0', 'files') @@ -390,6 +405,63 @@ def test_file_publish(self, test_params): assert res.get('filesize') == size2 assert res.get('filehash') == fhash2 + # test file upload from dexp2p share directory + fallbackpath = '' + if os.name == 'posix': + dex_path = os.environ['HOME'] + '/dexp2p/' + if not os.path.isdir(dex_path): + os.mkdir(dex_path) + if os.stat(dex_path).st_uid != os.getuid(): + raise NotADirectoryError("Directory not owned by current user: ", dex_path) + else: + appdatadir = os.environ['APPDATA'] + dex_path = appdatadir + '\\dexp2p\\' + if not os.path.isdir(dex_path): + raise NotADirectoryError("Directory does not exists or not owned by current user: ", dex_path) + fallbackpath = os.environ['HOMEDRIVE'] + '\\tmp\\dexp2p\\' + pubkey = rpc1.DEX_stats().get('publishable_pubkey') + file_shortname3 = 'file_' + randomstring(5) + file_fullname3 = dex_path + file_shortname3 + write_empty_file(file_fullname3, 10) + size = get_size(file_fullname3) + fhash = get_filehash(file_fullname3) + res = rpc1.DEX_publish(file_shortname3, '0') + assert res.get('result') == 'success' + assert res.get('fname') == file_shortname3 + assert res.get('filesize') == size + assert res.get('filehash') == fhash + time.sleep(20) + res = rpc2.DEX_subscribe(file_shortname3, '0', '0', pubkey) + assert res.get('result') == 'success' + assert res.get('fname') == file_shortname3 + assert res.get('filesize') == size + assert res.get('filehash') == fhash + + # Not actual atm + # # Check fallback to tmp dir on Windows + # if os.name != 'posix': + # for root, dirs, files in os.walk(dex_path, topdown=False): + # for name in files: + # os.remove(os.path.join(root, name)) + # for name in dirs: + # os.rmdir(os.path.join(root, name)) + # file_shortname4 = 'file_' + randomstring(5) + # file_fullname4 = fallbackpath + file_shortname4 + # write_empty_file(file_fullname4, 10) + # size = get_size(file_fullname4) + # fhash = get_filehash(file_fullname4) + # res = rpc1.DEX_publish(file_shortname4, '0') + # assert res.get('result') == 'success' + # assert res.get('fname') == file_shortname4 + # assert res.get('filesize') == size + # assert res.get('filehash') == fhash + # time.sleep(20) + # res = rpc2.DEX_subscribe(file_shortname4, '0', '0', pubkey) + # assert res.get('result') == 'success' + # assert res.get('fname') == file_shortname4 + # assert res.get('filesize') == size + # assert res.get('filehash') == fhash + def test_dex_encryption(self, test_params): rpc1 = test_params.get('node1').get('rpc') message = randomstring(15) @@ -449,3 +521,30 @@ def test_dex_anonsend(self, test_params): if match.get('id') == msg_id: assert match.get('anonmsg') == message assert match.get('anonsender') == pub + + def test_dex_zero_broadcast(self, test_params): # zero length payload + rpc1 = test_params.get('node1').get('rpc') + rpc2 = test_params.get('node1').get('rpc') + payload = '' + taga = randomstring(15) + tagb = randomstring(15) + priority = '1' + pubkey = rpc1.DEX_stats().get('publishable_pubkey') + + res = rpc1.DEX_broadcast(payload, priority, taga, tagb, pubkey) + assert not res # should have empty response + res = rpc1.DEX_broadcast(payload, priority, '', '', '') + assert not res + res = rpc1.DEX_broadcast(payload, priority, taga, '', '') + assert not res + time.sleep(15) + + # no messages should be listed + res = rpc1.DEX_list('', '1', taga, tagb, '') + assert not res.get('matches') + res = rpc1.DEX_list('', '1', taga, '', '') + assert not res.get('matches') + res = rpc2.DEX_list('', '1', taga, tagb, '') + assert not res.get('matches') + res = rpc2.DEX_list('', '1', taga, '', '') + assert not res.get('matches') \ No newline at end of file diff --git a/qa/pytest_komodo/start_set_dex.bat b/qa/pytest_komodo/start_set_dex.bat new file mode 100644 index 00000000000..acf88c2e6b2 --- /dev/null +++ b/qa/pytest_komodo/start_set_dex.bat @@ -0,0 +1,18 @@ +set CLIENTS=2 +set CHAIN=TONYCI +set TEST_ADDY0=RPWhA4f4ZTZxNi5K36bcwsWdVjSVDSjUnd +set TEST_WIF0=UpcQympViQpLmv1WzMwszKPrmKUa28zsv8pdLCMgNMXDFBBBKxCN +set TEST_PUBKEY0=02f0ec2d3da51b09e4fc8d9ba334c275b02b3ab6f22ce7be0ea5059cbccbd1b8c7 +set TEST_ADDY1=RHoTHYiHD8TU4k9rbY4Aoj3ztxUARMJikH +set TEST_WIF1=UwmmwgfXwZ673brawUarPzbtiqjsCPWnG311ZRAL4iUCZLBLYeDu +set TEST_PUBKEY1=0285f68aec0e2f8b5e817d71a2a20a1fda74ea9943c752a13136a3a30fa49c0149 +set TEST_ADDY2=RB6fgrPX4ANGTtMUVud6aRh5cFy7TqVein +set TEST_WIF2=UqMLYDNVtYLPKZ2CcqeBU1FcYNHkknVZaMuQRqqZWXyPmSuJw3kr +set TEST_PUBKEY2=02e9b141e1c251a942f77df10fa4de00f53f8ab2a6d5341bbaf842c95e674e92e9 +set CHAIN_MODE=DEX2 +set IS_BOOTSTRAP_NEEDED=True +set BOOTSTRAP_URL=https://sirseven.me/share/bootstrap.tar.gz + +python.exe chainstart.py + +python.exe -m pytest -s -vv modules\test_dexp2p.py modules\test_dexp2p_e2e.py diff --git a/qa/pytest_komodo/start_sync_ci.bat b/qa/pytest_komodo/start_sync_ci.bat new file mode 100644 index 00000000000..7406862562d --- /dev/null +++ b/qa/pytest_komodo/start_sync_ci.bat @@ -0,0 +1,14 @@ +set CLIENTS=1 +set CHAIN=%1 +set TEST_ADDY0=RPWhA4f4ZTZxNi5K36bcwsWdVjSVDSjUnd +set TEST_WIF0=UpcQympViQpLmv1WzMwszKPrmKUa28zsv8pdLCMgNMXDFBBBKxCN +set TEST_PUBKEY0=02f0ec2d3da51b09e4fc8d9ba334c275b02b3ab6f22ce7be0ea5059cbccbd1b8c7 +set CHAIN_MODE=REGULAR +set IS_BOOTSTRAP_NEEDED=%2 +set BOOTSTRAP_URL=%3 +set NOTARIZATIONS=True +set BLOCKTIME_AVR=60 + +python.exe chainstart.py + +python.exe -m pytest sync\test_sync.py -s -vv diff --git a/qa/pytest_komodo/sync/pytest_util.py b/qa/pytest_komodo/sync/pytest_util.py new file mode 100644 index 00000000000..1850475bde2 --- /dev/null +++ b/qa/pytest_komodo/sync/pytest_util.py @@ -0,0 +1,52 @@ +import os +import requests +import json + + +def env_get(var, default): + try: + res = os.environ[var] + except KeyError: + res = default + return res + + +def get_chainstate(proxy): + vals = {} + res = proxy.getinfo() + vals.update({'synced': res.get('synced')}) + vals.update({'notarized': res.get('notarized')}) + vals.update({'blocks': res.get('blocks')}) + vals.update({'longestchain': res.get('longestchain')}) + return vals + + +def get_notary_stats(): + api = "https://komodostats.com/api/notary/summary.json" + local = "notary.json" + data = requests.get(api).json() + with open(local, 'w') as lf: + lf.write(str(data)) + return data + + +def check_notarized(proxy, api_stats, coin, blocktime=60): + maxblocksdiff = round(1500 / blocktime) + daemon_stats = proxy.getinfo() + notarizations = {} + for item in api_stats: + if item.get('ac_name') == coin: + notarizations = item + if not notarizations: + raise BaseException("Chain notary data not found") + if daemon_stats['notarized'] == notarizations['notarized']: + assert daemon_stats['notarizedhash'] == notarizations['notarizedhash'] + assert daemon_stats['notarizedtxid'] == notarizations['notarizedtxid'] + return True + elif abs(daemon_stats['notarazied'] - notarizations['notarized']) >= maxblocksdiff: + return False + else: + assert daemon_stats['notarized'] + assert daemon_stats['notarizedhash'] != '0000000000000000000000000000000000000000000000000000000000000000' + assert daemon_stats['notarizedtxid'] != '0000000000000000000000000000000000000000000000000000000000000000' + return True diff --git a/qa/pytest_komodo/sync/test_sync.py b/qa/pytest_komodo/sync/test_sync.py new file mode 100644 index 00000000000..ecb53f4478b --- /dev/null +++ b/qa/pytest_komodo/sync/test_sync.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 SuperNET developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://www.opensource.org/licenses/mit-license.php. + +import pytest +import time +from pytest_util import env_get, get_chainstate, check_notarized, get_notary_stats + + +@pytest.mark.usefixtures("proxy_connection") +class TestChainSync: + + def test_base_sync(self, test_params): + rpc = test_params.get('node1').get('rpc') + # Get ENV test params + coin = env_get('CHAIN', 'KMD') + sync_timeout = env_get('TIMEOUT', 86400) + blocktime = int(env_get('BLOCKTIME_AVR', 60)) + check_notarizations = env_get('NOTARIZATIONS', False) + + # Main loop + start_time = time.time() + current_time = start_time + timeout = start_time + sync_timeout + warnings = 0 + while timeout > current_time: + values = get_chainstate(rpc) + if values.get('synced'): + print('Chain synced') + assert values.get('blocks') == values.get('longestchain') + if check_notarizations: + notarystats = get_notary_stats() + assert check_notarized(rpc, notarystats, coin, blocktime) + print("Notarization check passed") + break + print('Waiting synchronization...') + time.sleep(blocktime) + current_blocks = get_chainstate(rpc).get('blocks') + try: + assert current_blocks > values.get('blocks') + warnings = 0 + except AssertionError as e: + warnings += 1 + print("Synchronization might be stuck ", e) + if warnings >= 5: + raise TimeoutError("Synchronization stuck on block: ", current_blocks) diff --git a/qa/rpc-tests/cc_pytest/ci_setup.sh b/qa/rpc-tests/cc_pytest/ci_setup.sh new file mode 100755 index 00000000000..ee90322190e --- /dev/null +++ b/qa/rpc-tests/cc_pytest/ci_setup.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# chains start script params +export CLIENTS=2 +export CHAIN="TONYCI" +export TEST_ADDY="RPWhA4f4ZTZxNi5K36bcwsWdVjSVDSjUnd" +export TEST_WIF="UpcQympViQpLmv1WzMwszKPrmKUa28zsv8pdLCMgNMXDFBBBKxCN" +export TEST_PUBKEY="02f0ec2d3da51b09e4fc8d9ba334c275b02b3ab6f22ce7be0ea5059cbccbd1b8c7" +export TEST_ADDY2="RHoTHYiHD8TU4k9rbY4Aoj3ztxUARMJikH" +export TEST_WIF2="UwmmwgfXwZ673brawUarPzbtiqjsCPWnG311ZRAL4iUCZLBLYeDu" +export TEST_PUBKEY2="0285f68aec0e2f8b5e817d71a2a20a1fda74ea9943c752a13136a3a30fa49c0149" +export CHAIN_MODE="REGULAR" +export IS_BOOTSTRAP_NEEDED="True" +export BOOTSTRAP_URL="http://159.69.45.70/bootstrap.tar.gz" + +# starting the chains +python3 start_chains.py + +TEST_ID = $1 +# starting the tests +python3 -m pytest $1 -s diff --git a/src/Makefile.am b/src/Makefile.am index 6de96774f1d..23cff51cba9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -187,6 +187,7 @@ BITCOIN_CORE_H = \ crypto/haraka_portable.h \ crypto/verus_hash.h \ deprecation.h \ + fs.h \ hash.h \ httprpc.h \ httpserver.h \ @@ -245,6 +246,7 @@ BITCOIN_CORE_H = \ txdb.h \ txmempool.h \ ui_interface.h \ + util/asmap.h \ uint256.h \ uint252.h \ undo.h \ @@ -290,24 +292,17 @@ libbitcoin_server_a_SOURCES = \ cc/import.cpp \ cc/importgateway.cpp \ cc/CCassetsCore.cpp \ - cc/old/CCassetsCore_v0.cpp \ - cc/CCassetstx.cpp \ - cc/old/CCassetstx_v0.cpp \ cc/CCcustom.cpp \ cc/CCtx.cpp \ cc/CCutils.cpp \ - cc/CCvalidation.cpp \ cc/CCtokens.cpp \ - cc/old/CCtokens_v0.cpp \ cc/assets.cpp \ - cc/old/assets_v0.cpp \ cc/faucet.cpp \ cc/rewards.cpp \ cc/dice.cpp \ cc/lotto.cpp \ cc/fsm.cpp \ cc/heir.cpp \ - cc/old/heir_v0.cpp \ cc/oracles.cpp \ cc/prices.cpp \ cc/pegs.cpp \ @@ -317,11 +312,9 @@ libbitcoin_server_a_SOURCES = \ cc/channels.cpp \ cc/auction.cpp \ cc/betprotocol.cpp \ - cc/pricesfeed.cpp \ - cc/priceslibs/cjsonpointer.cpp \ - cc/kogs.cpp \ chain.cpp \ checkpoints.cpp \ + fs.cpp \ crosschain.cpp \ crosschain_authority.cpp \ crypto/haraka.h \ @@ -353,9 +346,6 @@ libbitcoin_server_a_SOURCES = \ rpc/net.cpp \ rpc/rawtransaction.cpp \ rpc/server.cpp \ - rpc/tokensrpc.cpp \ - rpc/pricesrpc.cpp \ - rpc/marmararpc.cpp \ script/serverchecker.cpp \ script/sigcache.cpp \ timedata.cpp \ @@ -402,6 +392,9 @@ libbitcoin_wallet_a_SOURCES = \ transaction_builder.cpp \ wallet/rpcdisclosure.cpp \ wallet/rpcdump.cpp \ + cc/CCtokens.cpp \ + cc/CCassetsCore.cpp \ + cc/CCassetstx.cpp \ cc/CCtx.cpp \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ @@ -506,9 +499,7 @@ libbitcoin_common_a_SOURCES = \ script/standard.cpp \ transaction_builder.cpp \ cc/CCtokenutils.cpp \ - cc/old/CCtokenutils_v0.cpp \ cc/CCutilbits.cpp \ - gmp_i64.c \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) @@ -533,6 +524,7 @@ libbitcoin_util_a_SOURCES = \ utilmoneystr.cpp \ utilstrencodings.cpp \ utiltime.cpp \ + util/asmap.cpp \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) @@ -540,6 +532,10 @@ if GLIBC_BACK_COMPAT libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp endif +if ENABLE_TESTS +libbitcoin_server_a_SOURCES += rpc/testtransactions.cpp +endif + # cli: zcash-cli libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) @@ -767,7 +763,6 @@ clean-local: -$(MAKE) -C secp256k1 clean -$(MAKE) -C snark clean -$(MAKE) -C univalue clean - -$(MAKE) -C cryptoconditions clean rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno -rm -f config.h diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index ec3a4a12e29..05b13225454 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -1,3 +1,6 @@ +# Copyright (c) 2016-2020 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://www.opensource.org/licenses/mit-license.php . TESTS += komodo-gtest bin_PROGRAMS += komodo-gtest diff --git a/src/Makefile.ktest.include b/src/Makefile.ktest.include index 740a48025d7..bfbccc196ad 100644 --- a/src/Makefile.ktest.include +++ b/src/Makefile.ktest.include @@ -12,7 +12,9 @@ komodo_test_SOURCES = \ test-komodo/test_parse_notarisation.cpp \ test-komodo/test_buffered_file.cpp \ test-komodo/test_sha256_crypto.cpp \ - test-komodo/test_script_standard_tests.cpp + test-komodo/test_script_standard_tests.cpp \ + test-komodo/test_addrman.cpp \ + test-komodo/test_netbase_tests.cpp komodo_test_CPPFLAGS = $(komodod_CPPFLAGS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index ecb2d2d36fb..2f0346b57a4 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -97,7 +97,6 @@ BITCOIN_TESTS =\ test/skiplist_tests.cpp \ test/test_bitcoin.cpp \ test/test_bitcoin.h \ - test/timedata_tests.cpp \ test/torcontrol_tests.cpp \ test/transaction_tests.cpp \ test/uint256_tests.cpp \ diff --git a/src/ac/etomic b/src/ac/etomic deleted file mode 100755 index 76eb0191c90..00000000000 --- a/src/ac/etomic +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=ETOMIC $1 $2 $3 $4 $5 $6 diff --git a/src/ac/k64 b/src/ac/k64 deleted file mode 100755 index a3b3bc83571..00000000000 --- a/src/ac/k64 +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=K64 $1 $2 $3 $4 $5 $6 diff --git a/src/ac/ksb b/src/ac/ksb deleted file mode 100755 index 450c3b3f6ec..00000000000 --- a/src/ac/ksb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=KSB $1 $2 $3 $4 $5 $6 diff --git a/src/ac/kv b/src/ac/kv deleted file mode 100755 index 997fccb33bb..00000000000 --- a/src/ac/kv +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=KV $1 $2 $3 $4 $5 $6 diff --git a/src/ac/our b/src/ac/our deleted file mode 100755 index 66c77447641..00000000000 --- a/src/ac/our +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=OUR $1 $2 $3 $4 $5 $6 diff --git a/src/ac/wlc b/src/ac/wlc deleted file mode 100755 index ef7c47b9032..00000000000 --- a/src/ac/wlc +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=WLC $1 $2 $3 $4 $5 $6 diff --git a/src/ac/zexo b/src/ac/zexo deleted file mode 100755 index b6fd508f296..00000000000 --- a/src/ac/zexo +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=ZEXO $1 $2 $3 $4 $5 $6 diff --git a/src/addrman.cpp b/src/addrman.cpp index 1c8bf4b7cf4..f93ae12d1da 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -23,19 +23,25 @@ #include "serialize.h" #include "streams.h" -int CAddrInfo::GetTriedBucket(const uint256& nKey) const +int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector &asmap) const { uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetHash().GetCheapHash(); - uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetHash().GetCheapHash(); - return hash2 % ADDRMAN_TRIED_BUCKET_COUNT; + uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetHash().GetCheapHash(); + int tried_bucket = hash2 % ADDRMAN_TRIED_BUCKET_COUNT; + uint32_t mapped_as = GetMappedAS(asmap); + LogPrint("net", "IP %s mapped to AS%i belongs to tried bucket %i\n", ToStringIP(), mapped_as, tried_bucket); + return tried_bucket; } -int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src) const +int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::vector &asmap) const { - std::vector vchSourceGroupKey = src.GetGroup(); - uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << vchSourceGroupKey).GetHash().GetCheapHash(); + std::vector vchSourceGroupKey = src.GetGroup(asmap); + uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << vchSourceGroupKey).GetHash().GetCheapHash(); uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetHash().GetCheapHash(); - return hash2 % ADDRMAN_NEW_BUCKET_COUNT; + int new_bucket = hash2 % ADDRMAN_NEW_BUCKET_COUNT; + uint32_t mapped_as = GetMappedAS(asmap); + LogPrint("net", "IP %s mapped to AS%i belongs to new bucket %i\n", ToStringIP(), mapped_as, new_bucket); + return new_bucket; } int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const @@ -175,7 +181,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId) assert(info.nRefCount == 0); // which tried bucket to move the entry to - int nKBucket = info.GetTriedBucket(nKey); + int nKBucket = info.GetTriedBucket(nKey, m_asmap); int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket); // first make space to add it (the existing tried entry there is moved to new, deleting whatever is there). @@ -191,7 +197,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId) nTried--; // find which new bucket it belongs to - int nUBucket = infoOld.GetNewBucket(nKey); + int nUBucket = infoOld.GetNewBucket(nKey, m_asmap); int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket); ClearNew(nUBucket, nUBucketPos); assert(vvNew[nUBucket][nUBucketPos] == -1); @@ -268,7 +274,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP if (pinfo) { // periodically update nTime - bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); + bool fCurrentlyOnline = (GetTime() - addr.nTime < 24 * 60 * 60); int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty)) pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty); @@ -301,7 +307,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP fNew = true; } - int nUBucket = pinfo->GetNewBucket(nKey, source); + int nUBucket = pinfo->GetNewBucket(nKey, source, m_asmap); int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket); if (vvNew[nUBucket][nUBucketPos] != nId) { bool fInsert = vvNew[nUBucket][nUBucketPos] == -1; @@ -453,7 +459,7 @@ int CAddrMan::Check_() if (vvTried[n][i] != -1) { if (!setTried.count(vvTried[n][i])) return -11; - if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey) != n) + if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey, m_asmap) != n) return -17; if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i) return -18; @@ -530,3 +536,30 @@ void CAddrMan::Connected_(const CService& addr, int64_t nTime) int CAddrMan::RandomInt(int nMax){ return GetRandInt(nMax); } + +std::vector CAddrMan::DecodeAsmap(fs::path path) +{ + std::vector bits; + FILE *filestr = fsbridge::fopen(path, "rb"); + CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); + if (file.IsNull()) { + LogPrintf("Failed to open asmap file from disk\n"); + return bits; + } + fseek(filestr, 0, SEEK_END); + int length = ftell(filestr); + LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length); + fseek(filestr, 0, SEEK_SET); + char cur_byte; + for (int i = 0; i < length; ++i) { + file >> cur_byte; + for (int bit = 0; bit < 8; ++bit) { + bits.push_back((cur_byte >> bit) & 1); + } + } + if (!SanityCheckASMap(bits)) { + LogPrintf("Sanity check of asmap file %s failed\n", path); + return {}; + } + return bits; +} diff --git a/src/addrman.h b/src/addrman.h index 0390b4e9ba3..28e07a82b13 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -20,12 +20,17 @@ #ifndef BITCOIN_ADDRMAN_H #define BITCOIN_ADDRMAN_H + #include "netbase.h" #include "protocol.h" #include "random.h" #include "sync.h" #include "timedata.h" #include "util.h" +#include "fs.h" +#include "clientversion.h" +#include "hash.h" +#include "netbase.h" #include #include @@ -97,25 +102,25 @@ class CAddrInfo : public CAddress } //! Calculate in which "tried" bucket this entry belongs - int GetTriedBucket(const uint256 &nKey) const; + int GetTriedBucket(const uint256 &nKey, const std::vector &asmap) const; //! Calculate in which "new" bucket this entry belongs, given a certain source - int GetNewBucket(const uint256 &nKey, const CNetAddr& src) const; + int GetNewBucket(const uint256 &nKey, const CNetAddr& src, const std::vector &asmap) const; //! Calculate in which "new" bucket this entry belongs, using its default source - int GetNewBucket(const uint256 &nKey) const + int GetNewBucket(const uint256 &nKey, const std::vector &asmap) const { - return GetNewBucket(nKey, source); + return GetNewBucket(nKey, source, asmap); } //! Calculate in which position of a bucket to store this entry. int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const; //! Determine whether the statistics about this entry are bad enough so that it can just be deleted - bool IsTerrible(int64_t nNow = GetAdjustedTime()) const; + bool IsTerrible(int64_t nNow = GetTime()) 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 = GetTime()) const; }; @@ -186,6 +191,7 @@ class CAddrInfo : public CAddress */ class CAddrMan { +friend class CAddrManTest; private: //! critical section to protect the inner data structures mutable CCriticalSection cs; @@ -264,9 +270,29 @@ class CAddrMan void Connected_(const CService &addr, int64_t nTime); public: + // Compressed IP->ASN mapping, loaded from a file when a node starts. + // Should be always empty if no file was provided. + // This mapping is then used for bucketing nodes in Addrman. + // + // If asmap is provided, nodes will be bucketed by + // AS they belong to, in order to make impossible for a node + // to connect to several nodes hosted in a single AS. + // This is done in response to Erebus attack, but also to generally + // diversify the connections every node creates, + // especially useful when a large fraction of nodes + // operate under a couple of cloud providers. + // + // If a new asmap was provided, the existing records + // would be re-bucketed accordingly. + std::vector m_asmap; + + // Read asmap from provided binary file + static std::vector DecodeAsmap(fs::path path); + + /** * serialized format: - * * version byte (currently 1) + * * version byte (1 for pre-asmap files, 2 for files including asmap version) * * 0x20 + nKey (serialized as if it were a vector, for backward compatibility) * * nNew * * nTried @@ -293,12 +319,12 @@ class CAddrMan * We don't use ADD_SERIALIZE_METHODS since the serialization and deserialization code has * very little in common. */ - template - void Serialize(Stream &s) const + template + void Serialize(Stream &s) const { LOCK(cs); - unsigned char nVersion = 1; + unsigned char nVersion = 2; s << nVersion; s << ((unsigned char)32); s << nKey; @@ -309,9 +335,9 @@ class CAddrMan s << nUBuckets; std::map mapUnkIds; int nIds = 0; - for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { - mapUnkIds[(*it).first] = nIds; - const CAddrInfo &info = (*it).second; + for (const auto& entry : mapInfo) { + mapUnkIds[entry.first] = nIds; + const CAddrInfo &info = entry.second; if (info.nRefCount) { assert(nIds != nNew); // this means nNew was wrong, oh ow s << info; @@ -319,8 +345,8 @@ class CAddrMan } } nIds = 0; - for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { - const CAddrInfo &info = (*it).second; + for (const auto& entry : mapInfo) { + const CAddrInfo &info = entry.second; if (info.fInTried) { assert(nIds != nTried); // this means nTried was wrong, oh ow s << info; @@ -341,6 +367,13 @@ class CAddrMan } } } + // Store asmap version after bucket entries so that it + // can be ignored by older clients for backward compatibility. + uint256 asmap_version; + if (m_asmap.size() != 0) { + asmap_version = SerializeHash(m_asmap); + } + s << asmap_version; } template @@ -349,7 +382,6 @@ class CAddrMan LOCK(cs); Clear(); - unsigned char nVersion; s >> nVersion; unsigned char nKeySize; @@ -379,16 +411,6 @@ class CAddrMan mapAddr[info] = n; info.nRandomPos = vRandom.size(); vRandom.push_back(n); - if (nVersion != 1 || nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) { - // In case the new table data cannot be used (nVersion unknown, or bucket count wrong), - // immediately try to give them a reference based on their primary source address. - int nUBucket = info.GetNewBucket(nKey); - int nUBucketPos = info.GetBucketPosition(nKey, true, nUBucket); - if (vvNew[nUBucket][nUBucketPos] == -1) { - vvNew[nUBucket][nUBucketPos] = n; - info.nRefCount++; - } - } } nIdCount = nNew; @@ -397,7 +419,7 @@ class CAddrMan for (int n = 0; n < nTried; n++) { CAddrInfo info; s >> info; - int nKBucket = info.GetTriedBucket(nKey); + int nKBucket = info.GetTriedBucket(nKey, m_asmap); int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket); if (vvTried[nKBucket][nKBucketPos] == -1) { info.nRandomPos = vRandom.size(); @@ -413,7 +435,9 @@ class CAddrMan } nTried -= nLost; - // Deserialize positions in the new table (if possible). + // Store positions in the new table buckets to apply later (if possible). + std::map entryToBucket; // Represents which entry belonged to which bucket when serializing + for (int bucket = 0; bucket < nUBuckets; bucket++) { int nSize = 0; s >> nSize; @@ -421,12 +445,38 @@ class CAddrMan int nIndex = 0; s >> nIndex; if (nIndex >= 0 && nIndex < nNew) { - CAddrInfo &info = mapInfo[nIndex]; + entryToBucket[nIndex] = bucket; + } + } + } + + uint256 supplied_asmap_version; + if (m_asmap.size() != 0) { + supplied_asmap_version = SerializeHash(m_asmap); + } + uint256 serialized_asmap_version; + if (nVersion > 1) { + s >> serialized_asmap_version; + } + + for (int n = 0; n < nNew; n++) { + CAddrInfo &info = mapInfo[n]; + int bucket = entryToBucket[n]; int nUBucketPos = info.GetBucketPosition(nKey, true, bucket); - if (nVersion == 1 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) { + if (nVersion == 2 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 && + info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) { + // Bucketing has not changed, using existing bucket positions for the new table + vvNew[bucket][nUBucketPos] = n; info.nRefCount++; - vvNew[bucket][nUBucketPos] = nIndex; - } + } else { + // In case the new table data cannot be used (nVersion unknown, bucket count wrong or new asmap), + // try to give them a reference based on their primary source address. + LogPrint("addrman", "Bucketing method was updated, re-bucketing addrman entries from disk\n"); + bucket = info.GetNewBucket(nKey, m_asmap); + nUBucketPos = info.GetBucketPosition(nKey, true, bucket); + if (vvNew[bucket][nUBucketPos] == -1) { + vvNew[bucket][nUBucketPos] = n; + info.nRefCount++; } } } @@ -451,6 +501,7 @@ class CAddrMan void Clear() { + LOCK(cs); std::vector().swap(vRandom); nKey = GetRandHash(); for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) { @@ -467,6 +518,8 @@ class CAddrMan nIdCount = 0; nTried = 0; nNew = 0; + mapInfo.clear(); + mapAddr.clear(); } CAddrMan() @@ -530,7 +583,7 @@ class CAddrMan } //! Mark an entry as accessible. - void Good(const CService &addr, int64_t nTime = GetAdjustedTime()) + void Good(const CService &addr, int64_t nTime = GetTime()) { { LOCK(cs); @@ -541,7 +594,7 @@ class CAddrMan } //! Mark an entry as connection attempted to. - void Attempt(const CService &addr, int64_t nTime = GetAdjustedTime()) + void Attempt(const CService &addr, int64_t nTime = GetTime()) { { LOCK(cs); @@ -580,7 +633,7 @@ class CAddrMan } //! Mark an entry as currently-connected-to. - void Connected(const CService &addr, int64_t nTime = GetAdjustedTime()) + void Connected(const CService &addr, int64_t nTime = GetTime()) { { LOCK(cs); diff --git a/src/alert.cpp b/src/alert.cpp index 4d832da7caa..e76f6a41108 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -117,7 +117,7 @@ uint256 CAlert::GetHash() const bool CAlert::IsInEffect() const { - return (GetAdjustedTime() < nExpiration); + return (GetTime() < nExpiration); } bool CAlert::Cancels(const CAlert& alert) const @@ -152,7 +152,7 @@ bool CAlert::RelayTo(CNode* pnode) const { if (AppliesTo(pnode->nVersion, pnode->strSubVer) || AppliesToMe() || - GetAdjustedTime() < nRelayUntil) + GetTime() < nRelayUntil) { pnode->PushMessage("alert", *this); return true; diff --git a/src/assetchains.json b/src/assetchains.json index 27fa9590d1b..97d4119b8be 100644 --- a/src/assetchains.json +++ b/src/assetchains.json @@ -23,10 +23,7 @@ "ac_cc": "2", "addressindex": "1", "spentindex": "1", - "addnode": [ - "142.93.136.89", - "195.201.22.89" - ] + "addnode": ["142.93.136.89", "195.201.22.89"] }, { "ac_name": "COQUICASH", @@ -36,9 +33,7 @@ "ac_halving": "420000", "ac_cc": "2", "ac_ccenable": "227,235,236,241", - "addnode": [ - "78.47.108.168" - ] + "addnode": ["78.47.108.168"] }, { "ac_name": "CRYPTO", @@ -48,10 +43,6 @@ "ac_name": "DEX", "ac_supply": "999999" }, - { - "ac_name": "ETOMIC", - "ac_supply": "100000000" - }, { "ac_name": "HODL", "ac_supply": "9999999" @@ -60,46 +51,21 @@ "ac_name": "ILN", "ac_supply": "10000000000", "ac_cc": "2", - "addnode": [ - "51.75.122.83" - ] + "addnode": ["51.75.122.83"] }, { "ac_name": "JUMBLR", "ac_supply": "999999" }, - { - "ac_name": "K64", - "ac_reward": "0", - "ac_supply": "64000777", - "ac_staked": "10", - "addnode": [ - "18.197.20.21" - ] - }, { "ac_name": "KOIN", "ac_supply": "125000000", - "addnode": [ - "3.0.32.10" - ] - }, - { - "ac_name": "KSB", - "ac_supply": "1000000000", - "ac_end": "1", - "ac_public": "1", - "addnode": [ - "37.187.225.231" - ] - }, - { - "ac_name": "KV", - "ac_supply": "1000000" + "addnode": ["3.0.32.10"] }, { "ac_name": "MESH", - "ac_supply": "1000007" + "ac_supply": "1000007", + "ac_ccactivate": "320000" }, { "ac_name": "MGW", @@ -111,10 +77,7 @@ "ac_reward": "100000000", "ac_cc": "3", "ac_staked": "10", - "addnode": [ - "138.201.136.145", - "95.217.44.58" - ] + "addnode": ["138.201.136.145", "95.217.44.58"] }, { "ac_name": "MSHARK", @@ -129,22 +92,6 @@ "ac_supply": "216000000", "ac_sapling": "5000000" }, - { - "ac_name": "OUR", - "ac_reward": "1478310502", - "ac_halving": "525600", - "ac_cc": "42", - "ac_supply": "100000000", - "ac_perc": "77700", - "ac_staked": "93", - "ac_pubkey": "02652a3f3e00b3a1875a918314f0bac838d6dd189a346fa623f5efe9541ac0b98c", - "ac_public": "1", - "addnode": [ - "51.255.195.65", - "217.182.129.38", - "37.187.225.231" - ] - }, { "ac_name": "PANGEA", "ac_supply": "999999" @@ -153,9 +100,7 @@ "ac_name": "PGT", "ac_supply": "10000000", "ac_end": "1", - "addnode": [ - "190.114.254.104" - ] + "addnode": ["190.114.254.104"] }, { "ac_name": "PIRATE", @@ -163,9 +108,7 @@ "ac_reward": "25600000000", "ac_halving": "77777", "ac_private": "1", - "addnode": [ - "136.243.102.225" - ] + "addnode": ["136.243.102.225"] }, { "ac_name": "REVS", @@ -182,15 +125,7 @@ "ac_reward": "100000000", "ac_cc": "3", "ac_staked": "10", - "addnode": [ - "138.201.136.145", - "95.217.44.58" - ] - }, - { - "ac_name": "SEC", - "ac_supply": "1000000000", - "ac_cc": "333" + "addnode": ["138.201.136.145", "95.217.44.58"] }, { "ac_name": "SUPERNET", @@ -211,43 +146,7 @@ "ac_cbmaturity": "1", "ac_sapling": "1", "earlytxid": "7e4a76259e99c9379551389e9f757fc5f46c33ae922a8644dc2b187af2a6adc1", - "addnode": [ - "157.230.45.184", - "165.22.52.123" - ] - }, - { - "ac_name": "VOTE2020", - "ac_supply": "123651638", - "ac_public": "1", - "addnode": [ - "95.213.238.98", - "38.91.101.236", - "2.56.153.50" - ] - }, - { - "ac_name": "VRSC", - "ac_algo": "verushash", - "ac_cc": "1", - "ac_veruspos": "50", - "ac_supply": "0", - "ac_eras": "3", - "ac_reward": "0,38400000000,2400000000", - "ac_halving": "1,43200,1051920", - "ac_decay": "100000000,0,0", - "ac_end": "10080,226080,0", - "ac_timelockgte": "19200000000", - "ac_timeunlockfrom": "129600", - "ac_timeunlockto": "1180800", - "addnode": [ - "185.25.48.236", - "185.64.105.111" - ] - }, - { - "ac_name": "WLC", - "ac_supply": "210000000" + "addnode": ["157.230.45.184", "165.22.52.123"] }, { "ac_name": "WLC21", @@ -255,34 +154,13 @@ "ac_reward": "190258751", "ac_staked": "90", "ac_public": "1", - "addnode": [ - "37.187.225.231", - "51.38.38.134" - ] - }, - { - "ac_name": "ZEXO", - "ac_reward": "1478310502", - "ac_halving": "525600", - "ac_cc": "42", - "ac_ccenable": "236", - "ac_supply": "100000000", - "ac_perc": "77700", - "ac_staked": "93", - "ac_pubkey": "02713bd85e054db923694b6b7a85306264edf4d6bd6d331814f2b40af444b3ebbc", - "ac_public": "1", - "addnode": [ - "195.201.20.230", - "80.240.17.222" - ] + "addnode": ["37.187.225.231", "51.38.38.134"] }, { "ac_name": "ZILLA", "ac_supply": "11000000", "ac_sapling": "5000000", - "addnode": [ - "51.68.215.104" - ] + "addnode": ["51.68.215.104"] }, { "ac_name": "STBL", @@ -291,9 +169,6 @@ "ac_public": "1", "ac_adaptivepow": "1", "ac_end": "1", - "addnode": [ - "142.93.173.53", - "68.183.74.250" - ] + "addnode": ["142.93.173.53", "68.183.74.250"] } ] diff --git a/src/assetchains.old b/src/assetchains.old index 9f1f5497a48..45574f2e7a8 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -4,43 +4,34 @@ delay=60 source pubkey.txt echo $pubkey -./komodod -pubkey=$pubkey -ac_name=AXO -ac_supply=200000000 -ac_ccactivate=130000 -addnode=95.213.238.98 & +./komodod -pubkey=$pubkey -ac_name=AXO -ac_supply=200000000 -ac_ccactivate=130000 -addnode=95.213.238.98 $1 & ./komodod -pubkey=$pubkey -ac_name=BET -ac_supply=999999 -addnode=95.213.238.98 $1 & ./komodod -pubkey=$pubkey -ac_name=BOTS -ac_supply=999999 -addnode=95.213.238.98 $1 & ./komodod -pubkey=$pubkey -ac_name=BTCH -ac_supply=20998641 -addnode=95.213.238.98 & -./komodod -pubkey=$pubkey -ac_name=CCL -ac_supply=200000000 -ac_end=1 -ac_cc=2 -addressindex=1 -spentindex=1 -addnode=142.93.136.89 -addnode=195.201.22.89 & -./komodod -pubkey=$pubkey -ac_name=COQUICASH -ac_supply=72000000 -ac_reward=7200000000 -ac_staked=50 -ac_halving=420000 -ac_cc=2 -ac_ccenable=227,235,236,241 -addnode=78.47.108.168 & +./komodod -pubkey=$pubkey -ac_name=CCL -ac_supply=200000000 -ac_end=1 -ac_cc=2 -addressindex=1 -spentindex=1 -addnode=142.93.136.89 -addnode=195.201.22.89 $1 & +./komodod -pubkey=$pubkey -ac_name=COQUICASH -ac_supply=72000000 -ac_reward=7200000000 -ac_staked=50 -ac_halving=420000 -ac_cc=2 -ac_ccenable=227,235,236,241 -addnode=78.47.108.168 $1 & ./komodod -pubkey=$pubkey -ac_name=CRYPTO -ac_supply=999999 -addnode=95.213.238.98 $1 & ./komodod -pubkey=$pubkey -ac_name=DEX -ac_supply=999999 -addnode=95.213.238.98 $1 & -./komodod -pubkey=$pubkey -ac_name=ETOMIC -ac_supply=100000000 -addnode=95.213.238.98 & ./komodod -pubkey=$pubkey -ac_name=HODL -ac_supply=9999999 -addnode=95.213.238.98 $1 & -~/hush3/src/komodod -pubkey=$pubkey -ac_name=HUSH3 -ac_sapling=1 -ac_reward=0,1125000000,562500000 -ac_halving=129,340000,840000 -ac_end=128,340000,5422111 -ac_eras=3 -ac_blocktime=150 -ac_cc=2 -ac_ccenable=228,234,235,236,241 -ac_founders=1 -ac_supply=6178674 -ac_perc=11111111 -clientname=GoldenSandtrout -addnode=188.165.212.101 -addnode=136.243.227.142 -addnode=5.9.224.250 -ac_cclib=hush3 -ac_script=76a9145eb10cf64f2bab1b457f1f25e658526155928fac88ac & -./komodod -pubkey=$pubkey -ac_name=ILN -ac_supply=10000000000 -ac_cc=2 -addressindex=1 -spentindex=1 -addnode=51.75.122.83 & +#~/hush3/src/komodod -pubkey=$pubkey -ac_name=HUSH3 -ac_sapling=1 -ac_reward=0,1125000000,562500000 -ac_halving=129,340000,840000 -ac_end=128,340000,5422111 -ac_eras=3 -ac_blocktime=150 -ac_cc=2 -ac_ccenable=228,234,235,236,241 -ac_founders=1 -ac_supply=6178674 -ac_perc=11111111 -clientname=GoldenSandtrout -addnode=188.165.212.101 -addnode=136.243.227.142 -addnode=5.9.224.250 -ac_cclib=hush3 -ac_script=76a9145eb10cf64f2bab1b457f1f25e658526155928fac88ac $1 & +./komodod -pubkey=$pubkey -ac_name=ILN -ac_supply=10000000000 -ac_cc=2 -addressindex=1 -spentindex=1 -addnode=51.75.122.83 $1 & ./komodod -pubkey=$pubkey -ac_name=JUMBLR -ac_supply=999999 -addnode=95.213.238.98 $1 & -./komodod -pubkey=$pubkey -ac_name=K64 -ac_supply=64000777 -ac_reward=0 -ac_staked=10 -addnode=18.197.20.211 & -./komodod -pubkey=$pubkey -ac_name=KOIN -ac_supply=125000000 -addnode=3.0.32.10 & -./komodod -pubkey=$pubkey -ac_name=KSB -ac_supply=1000000000 -ac_end=1 -ac_public=1 -addnode=37.187.225.231 & -./komodod -pubkey=$pubkey -ac_name=KV -ac_supply=1000000 -addnode=95.213.238.98 $1 & -./komodod -pubkey=$pubkey -ac_name=MESH -ac_supply=1000007 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=KOIN -ac_supply=125000000 -addnode=3.0.32.10 $1 & +./komodod -pubkey=$pubkey -ac_name=MESH -ac_supply=1000007 -ac_ccactivate=320000 -addnode=95.213.238.98 $1 & ./komodod -pubkey=$pubkey -ac_name=MGW -ac_supply=999999 -addnode=95.213.238.98 $1 & -./komodod -pubkey=$pubkey -ac_name=MORTY -ac_supply=90000000000 -ac_reward=100000000 -ac_cc=3 -ac_staked=10 -addnode=95.217.44.58 -addnode=138.201.136.145 & +./komodod -pubkey=$pubkey -ac_name=MORTY -ac_supply=90000000000 -ac_reward=100000000 -ac_cc=3 -ac_staked=10 -addnode=95.217.44.58 -addnode=138.201.136.145 $1 & ./komodod -pubkey=$pubkey -ac_name=MSHARK -ac_supply=1400000 -addnode=95.213.238.98 $1 & -./komodod -pubkey=$pubkey -ac_name=NINJA -ac_supply=100000000 -addnode=95.213.238.98 & -./komodod -pubkey=$pubkey -ac_name=OOT -ac_supply=216000000 -ac_sapling=5000000 -addnode=95.213.238.98 & -./komodod -pubkey=$pubkey -ac_name=OUR -ac_reward=1478310502 -ac_halving=525600 -ac_cc=42 -ac_supply=100000000 -ac_perc=77700 -ac_staked=93 -ac_pubkey=02652a3f3e00b3a1875a918314f0bac838d6dd189a346fa623f5efe9541ac0b98c -ac_public=1 -addnode=51.255.195.65 -addnode=217.182.129.38 -addnode=37.187.225.231 & +./komodod -pubkey=$pubkey -ac_name=NINJA -ac_supply=100000000 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=OOT -ac_supply=216000000 -ac_sapling=5000000 -addnode=95.213.238.98 $1 & ./komodod -pubkey=$pubkey -ac_name=PANGEA -ac_supply=999999 -addnode=95.213.238.98 $1 & -./komodod -pubkey=$pubkey -ac_name=PGT -ac_supply=10000000 -ac_end=1 -addnode=190.114.254.104 & -./komodod -pubkey=$pubkey -ac_name=PIRATE -ac_supply=0 -ac_reward=25600000000 -ac_halving=77777 -ac_private=1 -addnode=178.63.77.56 & +./komodod -pubkey=$pubkey -ac_name=PGT -ac_supply=10000000 -ac_end=1 -addnode=190.114.254.104 $1 & +./komodod -pubkey=$pubkey -ac_name=PIRATE -ac_supply=0 -ac_reward=25600000000 -ac_halving=77777 -ac_private=1 -addnode=178.63.77.56 $1 & ./komodod -pubkey=$pubkey -ac_name=REVS -ac_supply=1300000 -addnode=95.213.238.98 $1 & -./komodod -pubkey=$pubkey -ac_name=RFOX -ac_supply=1000000000 -ac_reward=100000000 -addnode=95.213.238.98 & -./komodod -pubkey=$pubkey -ac_name=RICK -ac_supply=90000000000 -ac_reward=100000000 -ac_cc=3 -ac_staked=10 -addnode=95.217.44.58 -addnode=138.201.136.145 & -./komodod -pubkey=$pubkey -ac_name=SEC -ac_cc=333 -ac_supply=1000000000 -addnode=185.148.145.43 & -./komodod -pubkey=$pubkey -ac_name=STBL -ac_supply=50000000 -ac_cc=1 -ac_public=1 -ac_adaptivepow=1 -ac_end=1 -addnode=142.93.173.53 -addnode=68.183.74.250 & +./komodod -pubkey=$pubkey -ac_name=RFOX -ac_supply=1000000000 -ac_reward=100000000 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=RICK -ac_supply=90000000000 -ac_reward=100000000 -ac_cc=3 -ac_staked=10 -addnode=95.217.44.58 -addnode=138.201.136.145 $1 & +./komodod -pubkey=$pubkey -ac_name=STBL -ac_supply=50000000 -ac_cc=1 -ac_public=1 -ac_adaptivepow=1 -ac_end=1 -addnode=142.93.173.53 -addnode=68.183.74.250 $1 & ./komodod -pubkey=$pubkey -ac_name=SUPERNET -ac_supply=816061 -addnode=95.213.238.98 $1 & -./komodod -pubkey=$pubkey -ac_name=THC -ac_supply=251253103 -ac_reward=360000000,300000000,240000000,180000000,150000000,90000000,0 -ac_staked=100 -ac_eras=7 -ac_end=500001,1000001,1500001,2000001,2500001,4500001,0 -ac_perc=233333333 -ac_cc=2 -ac_ccenable=229,236,240 -ac_script=2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401ccb8 -ac_founders=150 -ac_cbmaturity=1 -ac_sapling=1 -addnode=157.230.45.184 -addnode=165.22.52.123 -earlytxid=7e4a76259e99c9379551389e9f757fc5f46c33ae922a8644dc2b187af2a6adc1 & -~/VerusCoin/src/verusd -pubkey=$pubkey & -./komodod -pubkey=$pubkey -ac_public=1 -ac_name=VOTE2020 -ac_supply=123651638 -addnode=95.213.238.98 & -./komodod -pubkey=$pubkey -ac_name=WLC -ac_supply=210000000 -addnode=95.213.238.98 $1 & -./komodod -pubkey=$pubkey -ac_name=WLC21 -ac_supply=21000000 -ac_reward=190258751 -ac_staked=90 -ac_public=1 -addnode=37.187.225.231 -addnode=51.38.38.134 & -./komodod -pubkey=$pubkey -ac_name=ZEXO -ac_supply=100000000 -ac_reward=1478310502 -ac_halving=525600 -ac_cc=42 -ac_ccenable=236 -ac_perc=77700 -ac_staked=93 -ac_pubkey=02713bd85e054db923694b6b7a85306264edf4d6bd6d331814f2b40af444b3ebbc -ac_public=1 -addnode=80.240.17.222 & -./komodod -pubkey=$pubkey -ac_name=ZILLA -ac_supply=11000000 -ac_sapling=5000000 -addnode=51.68.215.104 & +./komodod -pubkey=$pubkey -ac_name=THC -ac_supply=251253103 -ac_reward=360000000,300000000,240000000,180000000,150000000,90000000,0 -ac_staked=100 -ac_eras=7 -ac_end=500001,1000001,1500001,2000001,2500001,4500001,0 -ac_perc=233333333 -ac_cc=2 -ac_ccenable=229,236,240 -ac_script=2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401ccb8 -ac_founders=150 -ac_cbmaturity=1 -ac_sapling=1 -addnode=157.230.45.184 -addnode=165.22.52.123 -earlytxid=7e4a76259e99c9379551389e9f757fc5f46c33ae922a8644dc2b187af2a6adc1 $1 & +#~/VerusCoin/src/verusd -pubkey=$pubkey $1 & +./komodod -pubkey=$pubkey -ac_name=WLC21 -ac_supply=21000000 -ac_reward=190258751 -ac_staked=90 -ac_public=1 -addnode=37.187.225.231 -addnode=51.38.38.134 $1 & +./komodod -pubkey=$pubkey -ac_name=ZILLA -ac_supply=11000000 -ac_sapling=5000000 -addnode=51.68.215.104 $1 & diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 0e0d6052d19..544972586aa 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -125,7 +125,7 @@ static int AppInitRPC(int argc, char* argv[]) return EXIT_FAILURE; } try { - ReadConfigFile(mapArgs, mapMultiArgs,1); + ReadConfigFile(mapArgs, mapMultiArgs); } catch (const std::exception& e) { fprintf(stderr,"Error reading configuration file: %s\n", e.what()); return EXIT_FAILURE; diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 4a1f0a0dda1..ef7b09a37f0 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -169,32 +169,30 @@ bool AppInit(int argc, char* argv[]) } try { - ReadConfigFile(mapArgs, mapMultiArgs,1); + ReadConfigFile(mapArgs, mapMultiArgs); } catch (const missing_zcash_conf& e) { fprintf(stderr, - (_("Before starting komodod, you need to create a configuration file:\n" - "%s\n" - "It can be completely empty! That indicates you are happy with the default\n" - "configuration of komodod. But requiring a configuration file to start ensures\n" - "that komodod won't accidentally compromise your privacy if there was a default\n" - "option you needed to change.\n" - "\n" - "You can look at the example configuration file for suggestions of default\n" - "options that you may want to change. It should be in one of these locations,\n" - "depending on how you installed Komodo:\n") + - _("- Source code: %s\n" - "- .deb package: %s\n")).c_str(), - GetConfigFile().string().c_str(), - "contrib/debian/examples/komodo.conf", - "/usr/share/doc/komodo/examples/komodo.conf"); + (_("Before starting komodod, you need to create a configuration file:\n" + "%s\n" + "It can be completely empty! That indicates you are happy with the default\n" + "configuration of komodod. But requiring a configuration file to start ensures\n" + "that komodod won't accidentally compromise your privacy if there was a default\n" + "option you needed to change.\n" + "\n" + "You can look at the example configuration file for suggestions of default\n" + "options that you may want to change. It should be in one of these locations,\n" + "depending on how you installed Komodo:\n") + + _("- Source code: %s\n" + "- .deb package: %s\n")).c_str(), + GetConfigFile().string().c_str(), + "contrib/debian/examples/komodo.conf", + "/usr/share/doc/komodo/examples/komodo.conf"); return false; } catch (const std::exception& e) { fprintf(stderr,"Error reading configuration file: %s\n", e.what()); return false; } - //extern uint16_t BITCOIND_RPCPORT; - //BITCOIND_RPCPORT = GetArg("-rpcport", BaseParams().RPCPort()); - + // Command-line RPC bool fCommandLine = false; for (int i = 1; i < argc; i++) diff --git a/src/cc/CCGateways.h b/src/cc/CCGateways.h index a5755dacf48..8793c0dc44a 100644 --- a/src/cc/CCGateways.h +++ b/src/cc/CCGateways.h @@ -22,12 +22,14 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); UniValue GatewaysBind(const CPubKey& pk, uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4); UniValue GatewaysDeposit(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount); +UniValue GatewaysClaim(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount); UniValue GatewaysWithdraw(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount); -UniValue GatewaysWithdrawSign(const CPubKey& pk, uint64_t txfee,uint256 lasttxid,std::string refcoin,std::string hex); -UniValue GatewaysMarkDone(const CPubKey& pk, uint64_t txfee,uint256 completetxid,std::string refcoin); +UniValue GatewaysPartialSign(const CPubKey& pk, uint64_t txfee,uint256 txidaddr,std::string refcoin,std::string hex); +UniValue GatewaysCompleteSigning(const CPubKey& pk, uint64_t txfee,uint256 txidaddr,std::string refcoin,std::string hex); +UniValue GatewaysMarkDone(const CPubKey& pk, uint64_t txfee,uint256 withdrawtxid,std::string refcoin); UniValue GatewaysPendingDeposits(const CPubKey& pk, uint256 bindtxid,std::string refcoin); -UniValue GatewaysPendingSignWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin); -UniValue GatewaysSignedWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin); +UniValue GatewaysPendingWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin); +UniValue GatewaysProcessedWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin); // CCcustom UniValue GatewaysInfo(uint256 bindtxid); diff --git a/src/cc/CCImportGateway.h b/src/cc/CCImportGateway.h index 7f409588e14..9be54f23a59 100644 --- a/src/cc/CCImportGateway.h +++ b/src/cc/CCImportGateway.h @@ -22,13 +22,14 @@ // CCcustom bool ImportGatewayValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn); bool ImportGatewayExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid); -UniValue ImportGatewayBind(const CPubKey& pk, uint64_t txfee,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4); -UniValue ImportGatewayDeposit(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t burnvout,std::string rawburntx,std::vectorproof,CPubKey destpub,uint64_t amount); -UniValue ImportGatewayWithdraw(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount); -UniValue ImportGatewayWithdrawSign(const CPubKey& pk, uint64_t txfee,uint256 lasttxid,std::string refcoin,std::string hex); -UniValue ImportGatewayMarkDone(const CPubKey& pk, uint64_t txfee,uint256 completetxid,std::string refcoin); -UniValue ImportGatewayPendingSignWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin); -UniValue ImportGatewaySignedWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin); +std::string ImportGatewayBind(uint64_t txfee,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4); +std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t burnvout,std::string rawburntx,std::vectorproof,CPubKey destpub,int64_t amount); +std::string ImportGatewayWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount); +std::string ImportGatewayPartialSign(uint64_t txfee,uint256 lasttxid,std::string refcoin, std::string hex); +std::string ImportGatewayCompleteSigning(uint64_t txfee,uint256 lasttxid,std::string refcoin,std::string hex); +std::string ImportGatewayMarkDone(uint64_t txfee,uint256 completetxid,std::string refcoin); +UniValue ImportGatewayPendingWithdraws(uint256 bindtxid,std::string refcoin); +UniValue ImportGatewayProcessedWithdraws(uint256 bindtxid,std::string refcoin); UniValue ImportGatewayExternalAddress(uint256 bindtxid,CPubKey pubkey); UniValue ImportGatewayDumpPrivKey(uint256 bindtxid,CKey key); UniValue ImportGatewayList(); diff --git a/src/cc/CCKogs.h b/src/cc/CCKogs.h deleted file mode 100644 index 0efb4dc890f..00000000000 --- a/src/cc/CCKogs.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CC_KOGS_H -#define CC_KOGS_H - -#include "CCinclude.h" - -bool KogsValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn); - -#endif diff --git a/src/cc/CCMarmara.h b/src/cc/CCMarmara.h index 4fef192c125..85f9175d17a 100644 --- a/src/cc/CCMarmara.h +++ b/src/cc/CCMarmara.h @@ -17,133 +17,31 @@ #ifndef CC_MARMARA_H #define CC_MARMARA_H -#include #include "CCinclude.h" +#include "../komodo_cJSON.h" #define MARMARA_GROUPSIZE 60 -//#define MARMARA_MINLOCK (1440 * 3 * 30) -//#define MARMARA_MAXLOCK (1440 * 24 * 30) -#define MARMARA_VINS (CC_MAXVINS / 2) -#define MARMARA_MAXENDORSERS 64 - -#define MARMARA_LOOP_TOLERANCE 100 - -//#define EVAL_MARMARA 0xef -#define MARMARA_V2LOCKHEIGHT (INT_MAX - 1) // lock to even - -#define MARMARA_CURRENCY "MARMARA" - -enum MARMARA_FUNCID : uint8_t { - MARMARA_COINBASE = 'C', - MARMARA_COINBASE_3X = 'E', - MARMARA_ACTIVATED = 'A', -// MARMARA_ACTIVATED_3X = 'F', - MARMARA_CREATELOOP = 'B', - MARMARA_REQUEST = 'R', - MARMARA_ISSUE = 'I', - MARMARA_TRANSFER = 'T', - MARMARA_SETTLE = 'S', - MARMARA_SETTLE_PARTIAL = 'D', - MARMARA_RELEASE = 'O', - MARMARA_LOOP = 'L', - MARMARA_LOCKED = 'K', - MARMARA_POOL = 'P' -}; - -const uint8_t MARMARA_OPRET_VERSION = 1; -const int32_t MARMARA_MARKER_VOUT = 1; -const int32_t MARMARA_BATON_VOUT = 0; -const int32_t MARMARA_REQUEST_VOUT = 0; -const int32_t MARMARA_OPENCLOSE_VOUT = 3; -const int32_t MARMARA_ACTIVATED_MARKER_AMOUNT = 5000; -const int32_t MARMARA_REQUESTTX_AMOUNT = 10000; -const int32_t MARMARA_CREATETX_AMOUNT = 20000; -const int32_t MARMARA_LOOP_MARKER_AMOUNT = 10000; - -//inline bool IS_REMOTE(const CPubKey &remotepk) { -// return remotepk.IsValid(); -//} - -inline bool IsFuncidOneOf(uint8_t funcid, const std::set & funcidSet) -{ - return funcidSet.find(funcid) != funcidSet.end(); -} - -const std::set MARMARA_ACTIVATED_FUNCIDS = { MARMARA_COINBASE, MARMARA_POOL, MARMARA_ACTIVATED, MARMARA_COINBASE_3X /*, MARMARA_ACTIVATED_3X*/ }; - - -struct SMarmaraCreditLoopOpret; -class CMarmaraOpretCheckerBase; -class CMarmaraActivatedOpretChecker; -class CMarmaraLockInLoopOpretChecker; - -// issuer and endorser optional params -struct SMarmaraOptParams { - uint8_t autoSettlement; - uint8_t autoInsurance; - int32_t disputeExpiresOffset; - uint8_t escrowOn; - CAmount blockageAmount; - int32_t avalCount; - - // default values: - SMarmaraOptParams() - { - autoSettlement = 1; - autoInsurance = 1; - - disputeExpiresOffset = 3 * 365 * 24 * 60; // 3 year if blocktime == 60 sec TODO: convert to normal date calculation as banks do - avalCount = 0; - escrowOn = false; - blockageAmount = 0LL; - } -}; - +#define MARMARA_MINLOCK (1440 * 3 * 30) +#define MARMARA_MAXLOCK (1440 * 24 * 30) +#define MARMARA_VINS 16 +#define EVAL_MARMARA 0xef extern uint8_t ASSETCHAINS_MARMARA; -//uint64_t komodo_block_prg(uint32_t nHeight); - -int32_t MarmaraGetbatontxid(std::vector &creditloop, uint256 &batontxid, uint256 txid); -UniValue MarmaraCreditloop(const CPubKey & remotepk, uint256 txid); -UniValue MarmaraSettlement(int64_t txfee, uint256 batontxid, CTransaction &settlementtx); -UniValue MarmaraLock(const CPubKey &remotepk, int64_t txfee, int64_t amount, const CPubKey ¶mPk); - -UniValue MarmaraPoolPayout(int64_t txfee, int32_t firstheight, double perc, char *jsonstr); // [[pk0, shares0], [pk1, shares1], ...] -UniValue MarmaraReceive(const CPubKey &remotepk, int64_t txfee, const CPubKey &senderpk, int64_t amount, const std::string ¤cy, int32_t matures, int32_t avalcount, uint256 batontxid, bool automaticflag); -UniValue MarmaraIssue(const CPubKey &remotepk, int64_t txfee, uint8_t funcid, const CPubKey &receiverpk, const struct SMarmaraOptParams ¶ms, uint256 approvaltxid, uint256 batontxid); -UniValue MarmaraInfo(const CPubKey &refpk, int32_t firstheight, int32_t lastheight, int64_t minamount, int64_t maxamount, const std::string ¤cy); -UniValue MarmaraNewActivatedAddress(CPubKey pk); -std::string MarmaraLock64(CWallet *pwalletMain, CAmount amount, int32_t nutxos); -UniValue MarmaraListActivatedAddresses(CWallet *pwalletMain); -std::string MarmaraReleaseActivatedCoins(CWallet *pwalletMain, const std::string &destaddr); -UniValue MarmaraPoSStat(int32_t beginHeight, int32_t endHeight); -std::string MarmaraUnlockActivatedCoins(CAmount amount); - -bool MarmaraValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn); - -// functions used in staking code in komodo_bitcoind.h -int32_t MarmaraSignature(uint8_t *utxosig, CMutableTransaction &txNew); -uint8_t MarmaraDecodeCoinbaseOpret(const CScript &scriptPubKey, CPubKey &pk, int32_t &height, int32_t &unlockht); -uint8_t MarmaraDecodeLoopOpret(const CScript scriptPubKey, struct SMarmaraCreditLoopOpret &loopData); -int32_t MarmaraGetStakeMultiplier(const CTransaction & tx, int32_t nvout); -int32_t MarmaraValidateStakeTx(const char *destaddr, const CScript &vintxOpret, const CTransaction &staketx, int32_t height); -struct komodo_staking *MarmaraGetStakingUtxos(struct komodo_staking *array, int32_t *numkp, int32_t *maxkp, uint8_t *hashbuf); - -int32_t MarmaraValidateCoinbase(int32_t height, CTransaction tx, std::string &errmsg); -void MarmaraRunAutoSettlement(int32_t height, std::vector & minersTransactions); -CScript MarmaraCreateDefaultCoinbaseScriptPubKey(int32_t nHeight, CPubKey minerpk); -CScript MarmaraCreatePoSCoinbaseScriptPubKey(int32_t nHeight, const CScript &defaultspk, const CTransaction &staketx); -CScript MarmaraCoinbaseOpret(uint8_t funcid, int32_t height, CPubKey pk); - -bool MyGetCCopret(const CScript &scriptPubKey, CScript &opret); - -// local decl: -//static bool CheckEitherOpRet(bool ccopretOnly, bool(*CheckOpretFunc)(const CScript &, CPubKey &), const CTransaction &tx, int32_t nvout, CScript &opret, CPubKey & pk); -//static bool IsLockInLoopOpret(const CScript &spk, CPubKey &pk); -//static bool IsActivatedOpret(const CScript &spk, CPubKey &pk); - -//int64_t AddMarmarainputs(bool(*CheckOpretFunc)(const CScript &, CPubKey &), CMutableTransaction &mtx, std::vector &pubkeys, const char *unspentaddr, CAmount amount, int32_t maxinputs); - - +uint64_t komodo_block_prg(uint32_t nHeight); +int32_t MarmaraGetcreatetxid(uint256 &createtxid,uint256 txid); +int32_t MarmaraGetbatontxid(std::vector &creditloop,uint256 &batontxid,uint256 txid); +UniValue MarmaraCreditloop(uint256 txid); +UniValue MarmaraSettlement(uint64_t txfee,uint256 batontxid); +UniValue MarmaraLock(uint64_t txfee,int64_t amount,int32_t height); + +UniValue MarmaraPoolPayout(uint64_t txfee,int32_t firstheight,double perc,char *jsonstr); // [[pk0, shares0], [pk1, shares1], ...] +UniValue MarmaraReceive(uint64_t txfee,CPubKey senderpk,int64_t amount,std::string currency,int32_t matures,uint256 batontxid,bool automaticflag); +UniValue MarmaraIssue(uint64_t txfee,uint8_t funcid,CPubKey receiverpk,int64_t amount,std::string currency,int32_t matures,uint256 approvaltxid,uint256 batontxid); +UniValue MarmaraInfo(CPubKey refpk,int32_t firstheight,int32_t lastheight,int64_t minamount,int64_t maxamount,std::string currency); + +bool MarmaraValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); + +// CCcustom +UniValue MarmaraInfo(); #endif diff --git a/src/cc/CCPegs.h b/src/cc/CCPegs.h index 1f9f617ac8a..78f1accac47 100644 --- a/src/cc/CCPegs.h +++ b/src/cc/CCPegs.h @@ -25,8 +25,7 @@ bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, UniValue PegsCreate(const CPubKey& pk,uint64_t txfee,int64_t amount,std::vector bindtxids); UniValue PegsFund(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount); UniValue PegsGet(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount); -UniValue PegsRedeem(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount); -UniValue PegsClose(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid); +UniValue PegsRedeem(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid); UniValue PegsLiquidate(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, uint256 liquidatetxid); UniValue PegsExchange(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount); UniValue PegsAccountHistory(const CPubKey& pk,uint256 pegstxid); diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 9d331786abd..554cf0ecade 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -26,7 +26,6 @@ extern CScript KOMODO_EARLYTXID_SCRIPTPUB; // #define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) // defined in komodo_defs.h #define PRICES_TXFEE 10000 -#define PRICES_MAXNAMELENGTH 64 #define PRICES_MAXLEVERAGE 777 #define PRICES_SMOOTHWIDTH 1 #define KOMODO_MAXPRICES 2048 // must be power of 2 and less than 8192 @@ -60,6 +59,5 @@ UniValue PricesList(uint32_t filter, CPubKey mypk); UniValue PricesGetOrderbook(); UniValue PricesRefillFund(int64_t amount); -// UniValue DenormPriceValue(const CAmount& amount, uint32_t mult); #endif diff --git a/src/cc/CCassets.h b/src/cc/CCassets.h index 11da0568625..7b31c094de8 100644 --- a/src/cc/CCassets.h +++ b/src/cc/CCassets.h @@ -25,38 +25,27 @@ #include "CCinclude.h" -#include "old/CCassets_v0.h" - -#define ASSETS_GLOBALADDR_VIN 1 -#define ASSETS_GLOBALADDR_VOUT 0 -#define ASSETS_MARKER_AMOUNT 10000 - - // CCcustom -bool AssetsValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn); +bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCassetsCore -vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t unit_price, std::vector origpubkey); -uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &unit_price, std::vector &origpubkey); -uint8_t SetAssetOrigpubkey(std::vector &origpubkey_out, CAmount &unit_price, const CTransaction &tx); -int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &remaining_units_out, std::vector &origpubkey_out, const CTransaction& tx, int32_t v, uint256 refassetid); -bool ValidateBidRemainder(CAmount unit_price, int64_t remaining_nValue, int64_t orig_nValue, int64_t received_nValue, int64_t paid_units); -bool ValidateAskRemainder(CAmount unit_price, int64_t remaining_assetoshis, int64_t orig_assetoshis, int64_t received_assetoshis, int64_t paid_nValue); -bool ValidateSwapRemainder(int64_t remaining_units, int64_t remaining_nValue, int64_t orig_nValue, int64_t received_nValue, int64_t paidprice, int64_t totalprice); -bool SetBidFillamounts(CAmount unit_price, int64_t &received_nValue, int64_t orig_nValue, int64_t &paid_units, int64_t orig_units, CAmount paid_unit_price); -bool SetAskFillamounts(CAmount unit_price, int64_t fill_assetoshis, int64_t orig_assetoshis, int64_t paid_nValue); -bool SetSwapFillamounts(CAmount unit_price, int64_t &paid, int64_t orig_nValue, int64_t &received, int64_t totalprice); // not implemented -int64_t AssetValidateBuyvin(struct CCcontract_info *cp, Eval* eval, int64_t &unit_price, std::vector &origpubkey_out, char *origCCaddr_out, char *origaddr_out, const CTransaction &tx, uint256 refassetid); -int64_t AssetValidateSellvin(struct CCcontract_info *cp, Eval* eval, int64_t &unit_price, std::vector &origpubkey_out, char *origCCaddr_out, char *origaddr_out, const CTransaction &tx, uint256 assetid); -//bool AssetsCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid); -//bool AssetsGetCCInputs(struct CCcontract_info *cpAssets, CAmount &tokensInputs, int64_t &assetsInputs, Eval* eval, const CTransaction &tx, uint256 assetid); -CAmount AssetsGetCCInputs(struct CCcontract_info *cp, const char *addr, const CTransaction &tx); -//uint256 AssetsGetPrevOrdertxid(const CTransaction &tx); -//CAmount AssetsGetUnitPrice(uint256 ordertxid); +vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector origpubkey); +uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey); +bool SetAssetOrigpubkey(std::vector &origpubkey,int64_t &price,const CTransaction &tx); +int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid); +bool ValidateBidRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice); +bool ValidateAskRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice); +bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice); +bool SetBidFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice); +bool SetAskFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice); +bool SetSwapFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice); +int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid); +int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid); +bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid); // CCassetstx //int64_t GetAssetBalance(CPubKey pk,uint256 tokenid); // --> GetTokenBalance() -//int64_t AddAssetInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 assetid, int64_t total, int32_t maxinputs); +int64_t AddAssetInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 assetid, int64_t total, int32_t maxinputs); UniValue AssetOrders(uint256 tokenid, CPubKey pubkey, uint8_t additionalEvalCode); //UniValue AssetInfo(uint256 tokenid); @@ -65,15 +54,12 @@ UniValue AssetOrders(uint256 tokenid, CPubKey pubkey, uint8_t additionalEvalCode //std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector destpubkey,int64_t total); //std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector destpubkey,int64_t total,int32_t evalcode); -UniValue CreateBuyOffer(const CPubKey &mypk, int64_t txfee, int64_t bidamount, uint256 assetid, int64_t numtokens); -UniValue CancelBuyOffer(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 bidtxid); -UniValue FillBuyOffer(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 bidtxid, int64_t fill_units, CAmount paid_unit_price); -UniValue CreateSell(const CPubKey &mypk, int64_t txfee, int64_t numtokens, uint256 assetid, int64_t askamount); -std::string CreateSwap(int64_t txfee, int64_t askamount, uint256 assetid, uint256 assetid2, int64_t pricetotal); -UniValue CancelSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 asktxid); -UniValue FillSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 assetid2, uint256 asktxid, int64_t fillamount, CAmount paid_unit_price); - -const char ccassets_log[] = "ccassets"; - +std::string CreateBuyOffer(int64_t txfee,int64_t bidamount,uint256 assetid,int64_t pricetotal); +std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid); +std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t fillamount); +std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t pricetotal); +std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 assetid2,int64_t pricetotal); +std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid); +std::string FillSell(int64_t txfee,uint256 assetid,uint256 assetid2,uint256 asktxid,int64_t fillamount); #endif diff --git a/src/cc/CCassetsCore.cpp b/src/cc/CCassetsCore.cpp index ad98d04e5a6..06d843b4010 100644 --- a/src/cc/CCassetsCore.cpp +++ b/src/cc/CCassetsCore.cpp @@ -14,10 +14,6 @@ ******************************************************************************/ #include "CCassets.h" -#include "CCtokens.h" -#include - - /* The SetAssetFillamounts() and ValidateAssetRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively. @@ -42,312 +38,284 @@ We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it. */ -// validate: -// unit_price received for a token >= seller's unit_price -// remaining_nValue calculated correctly -bool ValidateBidRemainder(CAmount unit_price, int64_t remaining_nValue, int64_t orig_nValue, int64_t received_nValue, int64_t paid_units) +bool ValidateBidRemainder(int64_t remaining_units,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidunits,int64_t totalunits) { - int64_t received_unit_price; - // int64_t new_unit_price = 0; - if (orig_nValue == 0 || received_nValue == 0 || paid_units == 0) + int64_t unitprice,recvunitprice,newunitprice=0; + if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s error any of these values can't be null: orig_nValue == %lld || received_nValue == %lld || paid_units == %lld\n", __func__, (long long)orig_nValue, (long long)received_nValue, (long long)paid_units); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); return(false); } - /* we do not need this check - else if (orig_units != (remaining_units + paid_units)) + else if ( totalunits != (remaining_units + paidunits) ) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s error orig_remaining_units %lld != %lld (remaining_units %lld + %lld paid_units)\n", __func__, (long long)orig_units, (long long)(remaining_units + paid_units), (long long)remaining_units, (long long)paid_units); + fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_units %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_units + paidunits),(long long)remaining_units,(long long)paidunits); return(false); - }*/ - else if (orig_nValue != (remaining_nValue + received_nValue)) + } + else if ( orig_nValue != (remaining_nValue + received_nValue) ) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s error orig_nValue %lld != %lld (remaining_nValue %lld + %lld received_nValue)\n", __func__, (long long)orig_nValue, (long long)(remaining_nValue - received_nValue), (long long)remaining_nValue, (long long)received_nValue); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); return(false); } else { - //unit_price = AssetsGetUnitPrice(ordertxid); - received_unit_price = (received_nValue / paid_units); - if (received_unit_price > unit_price) // can't satisfy bid by sending tokens of higher unit price than requested + //unitprice = (orig_nValue * COIN) / totalunits; + //recvunitprice = (received_nValue * COIN) / paidunits; + //if ( remaining_units != 0 ) + // newunitprice = (remaining_nValue * COIN) / remaining_units; + unitprice = (orig_nValue / totalunits); + recvunitprice = (received_nValue / paidunits); + if ( remaining_units != 0 ) + newunitprice = (remaining_nValue / remaining_units); + if ( recvunitprice < unitprice ) { - //fprintf(stderr, "%s error can't satisfy bid with higher unit price: received_unit_price %.8f > unit_price %.8f\n", __func__, (double)received_unit_price / (COIN), (double)unit_price / (COIN)); - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s error can't satisfy bid with higher unit price: received_unit_price %.8f > unit_price %.8f\n", __func__, (double)received_unit_price / (COIN), (double)unit_price / (COIN)); + fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); return(false); } - CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%s orig_nValue %lld received_nValue %lld, paid_units %lld, received_unit_price %.8f <= unit_price %.8f\n", __func__, (long long)orig_nValue, (long long)received_nValue, (long long)paid_units, (double)received_unit_price / (COIN), (double)unit_price / (COIN)); + fprintf(stderr,"ValidateAssetRemainder() orig %llu total %llu, recv %llu paid %llu,recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(long long)orig_nValue,(long long)totalunits,(long long)received_nValue,(long long)paidunits,(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); } return(true); } -// unit_price is unit price set by the bidder (maximum) -// received_nValue is coins amount received by token seller from the bidder -// orig_nValue is bid amount -// paid_units is tokens paid to the bidder -// orig_units it the tokens amount the bidder wants to buy -// paid_unit_price is unit_price that token seller sells his tokens for -bool SetBidFillamounts(CAmount unit_price, int64_t &received_nValue, int64_t orig_nValue, int64_t &paid_units, int64_t orig_units, CAmount paid_unit_price) +bool SetBidFillamounts(int64_t &received_nValue,int64_t &remaining_units,int64_t orig_nValue,int64_t &paidunits,int64_t totalunits) { - // int64_t remaining_nValue; - - //if (unit_price <= 0LL) - // unit_price = AssetsGetUnitPrice(ordertxid); - if (orig_units == 0) + int64_t remaining_nValue,unitprice; double dprice; + if ( totalunits == 0 ) { - received_nValue = paid_units = 0; + received_nValue = remaining_units = paidunits = 0; return(false); } - if (paid_units > orig_units) // not + if ( paidunits >= totalunits ) { - paid_units = 0; - // received_nValue = orig_nValue; - received_nValue = (paid_units * paid_unit_price); // as paid unit_price might be less than original unit_price - // remaining_units = 0; - fprintf(stderr, "%s not enough units!\n", __func__); - return(false); + paidunits = totalunits; + received_nValue = orig_nValue; + remaining_units = 0; + fprintf(stderr,"SetBidFillamounts() bid order totally filled!\n"); + return(true); } - //remaining_units = (orig_units - paid_units); + remaining_units = (totalunits - paidunits); //unitprice = (orig_nValue * COIN) / totalunits; //received_nValue = (paidunits * unitprice) / COIN; - //unit_price = (orig_nValue / orig_remaining_units); - - received_nValue = (paid_units * paid_unit_price); - fprintf(stderr, "%s orig_units.%lld - paid_units.%lld, (orig_value.%lld - received_value.%lld)\n", __func__, - (long long)orig_units, (long long)paid_units, (long long)orig_nValue, (long long)received_nValue); - if (unit_price > 0 && received_nValue > 0 && received_nValue <= orig_nValue) + unitprice = (orig_nValue / totalunits); + received_nValue = (paidunits * unitprice); + if ( unitprice > 0 && received_nValue > 0 && received_nValue <= orig_nValue ) { - CAmount remaining_nValue = (orig_nValue - received_nValue); - return (ValidateBidRemainder(unit_price, remaining_nValue, orig_nValue, received_nValue, paid_units)); - } - else - { - fprintf(stderr, "%s incorrect values: unit_price %lld > 0, orig_value.%lld >= received_value.%lld\n", __func__, - (long long)unit_price, (long long)orig_nValue, (long long)received_nValue); - return(false); - } + remaining_nValue = (orig_nValue - received_nValue); + printf("SetBidFillamounts() total.%llu - paid.%llu, remaining %llu <- %llu (%llu - %llu)\n",(long long)totalunits,(long long)paidunits,(long long)remaining_nValue,(long long)(orig_nValue - received_nValue),(long long)orig_nValue,(long long)received_nValue); + return(ValidateBidRemainder(remaining_units,remaining_nValue,orig_nValue,received_nValue,paidunits,totalunits)); + } else return(false); } -// unit_price is the mininum token price set by the token seller -// fill_assetoshis is tokens purchased -// orig_assetoshis is available tokens to sell -// paid_nValue is the paid coins calculated as fill_assetoshis * paid_unit_price -bool SetAskFillamounts(CAmount unit_price, int64_t fill_assetoshis, int64_t orig_assetoshis, int64_t paid_nValue) +bool SetAskFillamounts(int64_t &received_assetoshis,int64_t &remaining_nValue,int64_t orig_assetoshis,int64_t &paid_nValue,int64_t total_nValue) { - //int64_t remaining_assetoshis; - //double dunit_price; - - /*if (orig_nValue == 0) + int64_t remaining_assetoshis; double dunitprice; + if ( total_nValue == 0 ) { - fill_assetoshis = remaining_nValue = paid_nValue = 0; + received_assetoshis = remaining_nValue = paid_nValue = 0; return(false); } - if (paid_nValue >= orig_nValue) // TODO maybe if paid_nValue > orig_nValue just return error but not + if ( paid_nValue >= total_nValue ) { - // paid_nValue = orig_nValue; <- do not reset it - fill_assetoshis = orig_assetoshis; + paid_nValue = total_nValue; + received_assetoshis = orig_assetoshis; remaining_nValue = 0; - fprintf(stderr, "%s ask order totally filled!\n", __func__); + fprintf(stderr,"SetAskFillamounts() ask order totally filled!\n"); return(true); - }*/ - if (orig_assetoshis == 0) { - fprintf(stderr, "%s ask order empty!\n", __func__); - return false; - } - if (fill_assetoshis == 0) { - fprintf(stderr, "%s ask fill tokens is null!\n", __func__); - return false; - } - CAmount paid_unit_price = paid_nValue / fill_assetoshis; - //remaining_nValue = (orig_nValue - paid_nValue); - //CAmount unit_price = AssetsGetUnitPrice(ordertxid); - //remaining_nValue = orig_nValue - unit_price * fill_assetoshis; - // dunit_price = ((double)orig_nValue / orig_assetoshis); - // fill_assetoshis = (paid_nValue / dunit_price); // back conversion -> could be loss of value - fprintf(stderr, "%s paid_unit_price %lld fill_assetoshis %lld orig_assetoshis %lld unit_price %lld fill_assetoshis %lld)\n", __func__, - (long long)paid_unit_price, (long long)fill_assetoshis, (long long)orig_assetoshis, (long long)unit_price, (long long)fill_assetoshis); - if (paid_unit_price > 0 && fill_assetoshis <= orig_assetoshis) - { - CAmount remaining_assetoshis = (orig_assetoshis - fill_assetoshis); - if (remaining_assetoshis == 0) - fprintf(stderr, "%s ask order totally filled!\n", __func__); - return ValidateAskRemainder(unit_price, remaining_assetoshis, orig_assetoshis, fill_assetoshis, paid_nValue); } - else + remaining_nValue = (total_nValue - paid_nValue); + dunitprice = ((double)total_nValue / orig_assetoshis); + received_assetoshis = (paid_nValue / dunitprice); + fprintf(stderr,"SetAskFillamounts() remaining_nValue %.8f (%.8f - %.8f)\n",(double)remaining_nValue/COIN,(double)total_nValue/COIN,(double)paid_nValue/COIN); + fprintf(stderr,"SetAskFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); + if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis ) { - fprintf(stderr, "%s incorrect values paid_unit_price %lld > 0, fill_assetoshis %lld > 0 and <= orig_assetoshis %lld\n", __func__, - (long long)paid_unit_price, (long long)fill_assetoshis, (long long)orig_assetoshis); - return(false); - } + remaining_assetoshis = (orig_assetoshis - received_assetoshis); + return(ValidateAskRemainder(remaining_nValue,remaining_assetoshis,orig_assetoshis,received_assetoshis,paid_nValue,total_nValue)); + } else return(false); } -// validate: -// paid unit_price for a token <= the bidder's unit_price -// remaining coins calculated correctly -bool ValidateAskRemainder(CAmount unit_price, int64_t remaining_assetoshis, int64_t orig_assetoshis, int64_t received_assetoshis, int64_t paid_nValue) +bool ValidateAskRemainder(int64_t remaining_nValue,int64_t remaining_assetoshis,int64_t orig_assetoshis,int64_t received_assetoshis,int64_t paid_nValue,int64_t total_nValue) { - int64_t paid_unit_price; - //CAmount unit_price = AssetsGetUnitPrice(ordertxid); - //int64_t new_unit_price = 0; - - if (orig_assetoshis == 0 || received_assetoshis == 0 || paid_nValue == 0) + int64_t unitprice,recvunitprice,newunitprice=0; + if ( orig_assetoshis == 0 || received_assetoshis == 0 || paid_nValue == 0 || total_nValue == 0 ) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s error any of these values can't be null: orig_assetoshis == %lld || received_assetoshis == %lld || paid_nValue == %lld\n", __func__, (long long)orig_assetoshis, (long long)received_assetoshis, (long long)paid_nValue); + fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis == %llu || received_assetoshis == %llu || paid_nValue == %llu || total_nValue == %llu\n",(long long)orig_assetoshis,(long long)received_assetoshis,(long long)paid_nValue,(long long)total_nValue); return(false); } - // this check does not affect anything - //else if (orig_nValue != (remaining_nValue + paid_nValue)) - /*else if (orig_nValue != (remaining_nValue + received_assetoshis * unit_price)) // remaining_nValue is calced from the source unit_price, not paid unit_price + else if ( total_nValue != (remaining_nValue + paid_nValue) ) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s error orig_nValue %lld != %lld + (received_assetoshis %lld * unit_price %lld)\n", __func__, (long long)orig_nValue, (long long)remaining_nValue, (long long)received_assetoshis, (long long)unit_price); + fprintf(stderr,"ValidateAssetRemainder() total_nValue %llu != %llu (remaining_nValue %llu + %llu paid_nValue)\n",(long long)total_nValue,(long long)(remaining_nValue + paid_nValue),(long long)remaining_nValue,(long long)paid_nValue); return(false); - }*/ - else if (orig_assetoshis != (remaining_assetoshis + received_assetoshis)) + } + else if ( orig_assetoshis != (remaining_assetoshis + received_assetoshis) ) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s error orig_assetoshis %lld != %lld (remaining_nValue %lld + received_nValue %lld)\n", __func__, (long long)orig_assetoshis, (long long)(remaining_assetoshis - received_assetoshis), (long long)remaining_assetoshis, (long long)received_assetoshis); + fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_assetoshis,(long long)(remaining_assetoshis - received_assetoshis),(long long)remaining_assetoshis,(long long)received_assetoshis); return(false); } else { - // unit_price = (orig_nValue / orig_assetoshis); - paid_unit_price = (paid_nValue / received_assetoshis); - //if (remaining_nValue != 0) - // new_unit_price = (remaining_nValue / remaining_assetoshis); // for debug printing - if (paid_unit_price < unit_price) // can't pay for ask with lower unit price than requested + unitprice = (total_nValue / orig_assetoshis); + recvunitprice = (paid_nValue / received_assetoshis); + if ( remaining_nValue != 0 ) + newunitprice = (remaining_nValue / remaining_assetoshis); + if ( recvunitprice < unitprice ) { - //fprintf(stderr, "%s error can't pay for ask with lower price: paid_unit_price %.8f < %.8f unit_price\n", __func__, (double)paid_unit_price / COIN, (double)unit_price / COIN); - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s error can't pay for ask with lower price: paid_unit_price %.8f < unit_price %.8f\n", __func__, (double)paid_unit_price / COIN, (double)unit_price / COIN); - + fprintf(stderr,"ValidateAskRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); return(false); } - CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%s got paid_unit_price %.8f >= unit_price %.8f\n", __func__, (double)paid_unit_price / COIN, (double)unit_price / COIN); + fprintf(stderr,"ValidateAskRemainder() got recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); } return(true); } -// not implemented -bool SetSwapFillamounts(CAmount unit_price, int64_t &received_assetoshis, int64_t orig_assetoshis, int64_t &paid_assetoshis2, int64_t total_assetoshis2) +bool SetSwapFillamounts(int64_t &received_assetoshis,int64_t &remaining_assetoshis2,int64_t orig_assetoshis,int64_t &paid_assetoshis2,int64_t total_assetoshis2) { - int64_t remaining_assetoshis; double dunitprice; - if ( total_assetoshis2 == 0 ) - { - fprintf(stderr,"%s total_assetoshis2.0 orig_assetoshis.%lld paid_assetoshis2.%lld\n", __func__, (long long)orig_assetoshis,(long long)paid_assetoshis2); - received_assetoshis = paid_assetoshis2 = 0; - return(false); - } - if ( paid_assetoshis2 >= total_assetoshis2 ) - { - paid_assetoshis2 = total_assetoshis2; - received_assetoshis = orig_assetoshis; - // remaining_assetoshis2 = 0; - fprintf(stderr,"%s swap order totally filled!\n", __func__); - return(true); - } - // remaining_assetoshis2 = (total_assetoshis2 - paid_assetoshis2); - dunitprice = ((double)total_assetoshis2 / orig_assetoshis); - received_assetoshis = (paid_assetoshis2 / dunitprice); - fprintf(stderr,"%s (%lld - %lld)\n", __func__, (long long)total_assetoshis2/COIN, (long long)paid_assetoshis2/COIN); - fprintf(stderr,"%s unitprice %.8f received_assetoshis %lld orig %lld\n", __func__,dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); - if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis ) - { - remaining_assetoshis = (orig_assetoshis - received_assetoshis); - return ValidateAskRemainder(unit_price, remaining_assetoshis,orig_assetoshis,received_assetoshis,paid_assetoshis2); - } - else + int64_t remaining_assetoshis; double dunitprice; + if ( total_assetoshis2 == 0 ) + { + fprintf(stderr,"SetSwapFillamounts() total_assetoshis2.0 origsatoshis.%llu paid_assetoshis2.%llu\n",(long long)orig_assetoshis,(long long)paid_assetoshis2); + received_assetoshis = remaining_assetoshis2 = paid_assetoshis2 = 0; return(false); + } + if ( paid_assetoshis2 >= total_assetoshis2 ) + { + paid_assetoshis2 = total_assetoshis2; + received_assetoshis = orig_assetoshis; + remaining_assetoshis2 = 0; + fprintf(stderr,"SetSwapFillamounts() swap order totally filled!\n"); + return(true); + } + remaining_assetoshis2 = (total_assetoshis2 - paid_assetoshis2); + dunitprice = ((double)total_assetoshis2 / orig_assetoshis); + received_assetoshis = (paid_assetoshis2 / dunitprice); + fprintf(stderr,"SetSwapFillamounts() remaining_assetoshis2 %llu (%llu - %llu)\n",(long long)remaining_assetoshis2/COIN,(long long)total_assetoshis2/COIN,(long long)paid_assetoshis2/COIN); + fprintf(stderr,"SetSwapFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); + if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis ) + { + remaining_assetoshis = (orig_assetoshis - received_assetoshis); + return(ValidateAskRemainder(remaining_assetoshis2,remaining_assetoshis,orig_assetoshis,received_assetoshis,paid_assetoshis2,total_assetoshis2)); + } else return(false); } -bool ValidateSwapRemainder(int64_t remaining_price, int64_t remaining_nValue, int64_t orig_nValue, int64_t received_nValue, int64_t paidunits, int64_t totalunits) +bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidunits,int64_t totalunits) { - int64_t unitprice, recvunitprice, newunitprice = 0; - if (orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0) + int64_t unitprice,recvunitprice,newunitprice=0; + if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s orig_nValue == %lld || received_nValue == %lld || paidunits == %lld || totalunits == %lld\n", __func__, (long long)orig_nValue, (long long)received_nValue, (long long)paidunits, (long long)totalunits); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); return(false); } - else if (totalunits != (remaining_price + paidunits)) + else if ( totalunits != (remaining_price + paidunits) ) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s totalunits %lld != %lld (remaining_price %lld + %lld paidunits)\n", __func__, (long long)totalunits, (long long)(remaining_price + paidunits), (long long)remaining_price, (long long)paidunits); + fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_price %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_price + paidunits),(long long)remaining_price,(long long)paidunits); return(false); } - else if (orig_nValue != (remaining_nValue + received_nValue)) + else if ( orig_nValue != (remaining_nValue + received_nValue) ) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s orig_nValue %lld != %lld (remaining_nValue %lld + %lld received_nValue)\n", __func__, (long long)orig_nValue, (long long)(remaining_nValue - received_nValue), (long long)remaining_nValue, (long long)received_nValue); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); return(false); } else { unitprice = (orig_nValue * COIN) / totalunits; recvunitprice = (received_nValue * COIN) / paidunits; - if (remaining_price != 0) + if ( remaining_price != 0 ) newunitprice = (remaining_nValue * COIN) / remaining_price; - if (recvunitprice < unitprice) + if ( recvunitprice < unitprice ) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n", __func__, (double)recvunitprice / (COIN*COIN), (double)unitprice / (COIN*COIN), (double)newunitprice / (COIN*COIN)); + fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); return(false); } - fprintf(stderr, "%s recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n", __func__, (double)recvunitprice / (COIN*COIN), (double)unitprice / (COIN*COIN), (double)newunitprice / (COIN*COIN)); + fprintf(stderr,"ValidateAssetRemainder() recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); } return(true); } -vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t unit_price, std::vector origpubkey) +/* use EncodeTokenCreateOpRet instead: +CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector origpubkey,std::string name,std::string description) +{ + CScript opret; uint8_t evalcode = EVAL_ASSETS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description); + return(opret); +} +*/ + +vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector origpubkey) { vscript_t vopret; uint8_t evalcode = EVAL_ASSETS; - uint8_t version = 1; switch ( assetFuncId ) { //case 't': this cannot be here case 'x': case 'o': - vopret = E_MARSHAL(ss << evalcode << assetFuncId << version); + vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId); break; case 's': case 'b': case 'S': case 'B': - vopret = E_MARSHAL(ss << evalcode << assetFuncId << version << unit_price << origpubkey); + vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId << price << origpubkey); break; case 'E': case 'e': assetid2 = revuint256(assetid2); - vopret = E_MARSHAL(ss << evalcode << assetFuncId << version << assetid2 << unit_price << origpubkey); + vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId << assetid2 << price << origpubkey); break; default: - CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%s illegal funcid.%02x\n", __func__, assetFuncId); + fprintf(stderr,"EncodeAssetOpRet: illegal funcid.%02x\n", assetFuncId); + //opret << OP_RETURN; break; } return(vopret); } -uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &unit_price, std::vector &origpubkey) +/* it is for compatibility, do not use this for new contracts (use DecodeTokenCreateOpRet) +bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description) +{ + std::vector vopret; uint8_t evalcode,funcid,*script; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( script != 0 && vopret.size() > 2 && script[0] == EVAL_ASSETS && script[1] == 'c' ) + { + if ( E_UNMARSHAL(vopret,ss >> evalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description) != 0 ) + return(true); + } + return(0); +} */ + +uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey) { vscript_t vopretAssets; //, vopretAssetsStripped; - uint8_t *script, funcId = 0, assetsFuncId = 0, dummyAssetFuncId, dummyEvalCode, version; + uint8_t *script, funcId = 0, assetsFuncId = 0, dummyEvalCode, dummyAssetFuncId; uint256 dummyTokenid; std::vector voutPubkeysDummy; - std::vector oprets; + std::vector> oprets; tokenid = zeroid; assetid2 = zeroid; - unit_price = 0; + price = 0; assetsEvalCode = 0; assetsFuncId = 0; // First - decode token opret: - funcId = DecodeTokenOpRetV1(scriptPubKey, tokenid, voutPubkeysDummy, oprets); - GetOpReturnCCBlob(oprets, vopretAssets); + funcId = DecodeTokenOpRet(scriptPubKey, dummyEvalCode, tokenid, voutPubkeysDummy, oprets); + GetOpretBlob(oprets, OPRETID_ASSETSDATA, vopretAssets); - LOGSTREAMFN(ccassets_log, CCLOG_DEBUG2, stream << "from DecodeTokenOpRet returned funcId=" << (int)funcId << std::endl); + LOGSTREAM((char *)"ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() from DecodeTokenOpRet returned funcId=" << (int)funcId << std::endl); if (funcId == 0 || vopretAssets.size() < 2) { - LOGSTREAMFN(ccassets_log, CCLOG_DEBUG1, stream << "incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << std::endl); + LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << std::endl); return (uint8_t)0; } + //if (!E_UNMARSHAL(vopretAssets, { ss >> vopretAssetsStripped; })) { //strip string size + // std::cerr << "DecodeAssetTokenOpRet() could not unmarshal vopretAssetsStripped" << std::endl; + // return (uint8_t)0; + //} + // additional check to prevent crash if (vopretAssets.size() >= 2) { assetsEvalCode = vopretAssets.begin()[0]; assetsFuncId = vopretAssets.begin()[1]; - LOGSTREAMFN(ccassets_log, CCLOG_DEBUG2, stream << "assetsEvalCode=" << (int)assetsEvalCode << " funcId=" << (char)(funcId ? funcId : ' ') << " assetsFuncId=" << (char)(assetsFuncId ? assetsFuncId : ' ') << std::endl); + LOGSTREAM((char *)"ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() assetsEvalCode=" << (int)assetsEvalCode << " funcId=" << (char)(funcId ? funcId : ' ') << " assetsFuncId=" << (char)(assetsFuncId ? assetsFuncId : ' ') << std::endl); if (assetsEvalCode == EVAL_ASSETS) { @@ -355,23 +323,22 @@ uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCo switch (assetsFuncId) { case 'x': case 'o': - if (vopretAssets.size() == 3) // format is 'evalcode funcid version' + if (vopretAssets.size() == 2) // no data after 'evalcode assetFuncId' allowed { return(assetsFuncId); } break; case 's': case 'b': case 'S': case 'B': - if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> version; ss >> unit_price; ss >> origpubkey) != 0) + if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0) { - //fprintf(stderr,"DecodeAssetTokenOpRet() got price %lld\n",(long long)price); + //fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price); return(assetsFuncId); } break; case 'E': case 'e': - // not implemented yet - if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> version; ss >> assetid2; ss >> unit_price; ss >> origpubkey) != 0) + if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0) { - //fprintf(stderr,"DecodeAssetTokenOpRet() got price %lld\n",(long long)price); + //fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price); assetid2 = revuint256(assetid2); return(assetsFuncId); } @@ -382,20 +349,20 @@ uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCo } } - LOGSTREAMFN(ccassets_log, CCLOG_DEBUG1, stream << "no asset's payload or incorrect assets funcId or evalcode" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << " assetsEvalCode=" << assetsEvalCode << " assetsFuncId=" << assetsFuncId << std::endl); + LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() no asset's payload or incorrect assets funcId or evalcode" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << " assetsEvalCode=" << assetsEvalCode << " assetsFuncId=" << assetsFuncId << std::endl); return (uint8_t)0; } // extract sell/buy owner's pubkey from the opret -uint8_t SetAssetOrigpubkey(std::vector &origpubkey_out, CAmount &unit_price, const CTransaction &tx) +bool SetAssetOrigpubkey(std::vector &origpubkey,int64_t &price,const CTransaction &tx) { - uint256 assetid, assetid2; - uint8_t evalCode, funcid; + uint256 assetid,assetid2; + uint8_t evalCode; - if (tx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, unit_price, origpubkey_out)) != 0) - return funcid; - else - return 0; + if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey) != 0 ) + return(true); + else + return(false); } // Calculate seller/buyer's dest cc address from ask/bid tx funcid @@ -410,136 +377,118 @@ bool GetAssetorigaddrs(struct CCcontract_info *cp, char *origCCaddr, char *origN uint8_t evalCode; n = vintx.vout.size(); - if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 ) { - LOGSTREAMFN(ccassets_log, CCLOG_INFO, stream << "could not get vintx opreturn" << std::endl); + if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 ) return(false); - } + bool bGetCCaddr = false; struct CCcontract_info *cpTokens, tokensC; cpTokens = CCinit(&tokensC, EVAL_TOKENS); - if (vintxFuncId == 's' || vintxFuncId == 'S' || vintxFuncId == 'b' || vintxFuncId == 'B') { - cpTokens->evalcodeNFT = cp->evalcodeNFT; // add non-fungible evalcode if present - if (!GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey))) // tokens to single-eval token or token+nonfungible - return false; + if (vintxFuncId == 's' || vintxFuncId == 'S') { + // bGetCCaddr = GetCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); + cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present + bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible + } + else if (vintxFuncId == 'b' || vintxFuncId == 'B') { + cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present + bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible } else { - LOGSTREAMFN(ccassets_log, CCLOG_INFO, stream << "incorrect vintx funcid=" << (char)(vintxFuncId?vintxFuncId:' ') << std::endl); + std::cerr << "GetAssetorigaddrs incorrect vintx funcid=" << (char)(vintxFuncId?vintxFuncId:' ') << std::endl; return false; } - if (Getscriptaddress(origNormalAddr, CScript() << origpubkey << OP_CHECKSIG)) + if( bGetCCaddr && Getscriptaddress(origNormalAddr, CScript() << origpubkey << OP_CHECKSIG)) return(true); else return(false); } -int64_t AssetValidateCCvin(struct CCcontract_info *cpAssets, Eval* eval, char *origCCaddr_out, char *origaddr_out, const CTransaction &tx, int32_t vini, CTransaction &vinTx) +int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *origCCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx) { uint256 hashBlock; - uint256 assetid, assetid2; - uint256 vinAssetId, vinAssetId2; - int64_t tmpprice, vinPrice; - std::vector tmporigpubkey; - std::vector vinOrigpubkey; - uint8_t evalCode; - uint8_t vinEvalCode; - - char destaddr[KOMODO_ADDRESS_BUFSIZE], unspendableAddr[KOMODO_ADDRESS_BUFSIZE]; - - origaddr_out[0] = destaddr[0] = origCCaddr_out[0] = 0; - - uint8_t funcid = 0; - uint8_t vinFuncId = 0; - if (tx.vout.size() > 0) - funcid = DecodeAssetTokenOpRet(tx.vout.back().scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey); - else - return eval->Invalid("no vouts in tx"); + uint256 assetid, assetid2; + int64_t tmpprice; + std::vector tmporigpubkey; + uint8_t evalCode; + + char destaddr[64], unspendableAddr[64]; + + origaddr[0] = destaddr[0] = origCCaddr[0] = 0; + + uint8_t funcid = 0; + if (tx.vout.size() > 0) { + uint256 assetid, assetid2; + int64_t tmpprice; + std::vector tmporigpubkey; + uint8_t evalCode; + + funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey); + } if( tx.vin.size() < 2 ) return eval->Invalid("not enough for CC vins"); - else if(tx.vin[vini].prevout.n != ASSETS_GLOBALADDR_VOUT) // check gobal addr vout number == 0 + else if( tx.vin[vini].prevout.n != 0 ) return eval->Invalid("vin1 needs to be buyvin.vout[0]"); - else if(eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash, vinTx, hashBlock) == false) + else if( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash, vinTx,hashBlock) == 0 ) { - LOGSTREAMFN(ccassets_log, CCLOG_ERROR, stream << "cannot load vintx for vin=" << vini << " vintx id=" << tx.vin[vini].prevout.hash.GetHex() << std::endl); - return eval->Invalid("could not load previous tx or it has too few vouts"); + std::cerr << "AssetValidateCCvin() cannot load vintx for vin=" << vini << " vintx id=" << tx.vin[vini].prevout.hash.GetHex() << std::endl; + return eval->Invalid("always should find CCvin, but didnt"); } - else if (vinTx.vout.size() < 1 || (vinFuncId = DecodeAssetTokenOpRet(vinTx.vout.back().scriptPubKey, vinEvalCode, vinAssetId, vinAssetId2, vinPrice, vinOrigpubkey)) == 0) - return eval->Invalid("could not find assets opreturn in previous tx"); - // if fillSell or cancelSell --> should spend tokens from dual-eval token-assets global addr: - else if((funcid == 'S' || funcid == 'x') && - (tx.vin[vini].prevout.n >= vinTx.vout.size() || // check bounds - Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == false || - !GetTokensCCaddress(cpAssets, unspendableAddr, GetUnspendable(cpAssets, NULL)) || + // check source cc unspendable cc address: + // if fillSell or cancelSell --> should spend tokens from dual-eval token-assets unspendable addr + else if( (funcid == 'S' || funcid == 'x') && + (Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || + !GetTokensCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) || strcmp(destaddr, unspendableAddr) != 0)) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n", __func__, destaddr, (int)cpAssets->evalcode, unspendableAddr); + fprintf(stderr,"AssetValidateCCvin() cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); return eval->Invalid("invalid vin assets CCaddr"); } - // if fillBuy or cancelBuy --> should spend coins from asset cc global addr + // if fillBuy or cancelBuy --> should spend coins from asset unspendable addr else if ((funcid == 'B' || funcid == 'o') && - (tx.vin[vini].prevout.n >= vinTx.vout.size() || // check bounds - Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == false || - !GetCCaddress(cpAssets, unspendableAddr, GetUnspendable(cpAssets, NULL)) || + (Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || + !GetCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) || strcmp(destaddr, unspendableAddr) != 0)) { - CCLogPrintF(ccassets_log, CCLOG_ERROR, "%s cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", __func__, destaddr, (int)cpAssets->evalcode, unspendableAddr); + fprintf(stderr, "AssetValidateCCvin() cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); return eval->Invalid("invalid vin assets CCaddr"); } // end of check source unspendable cc address //else if ( vinTx.vout[0].nValue < 10000 ) // return eval->Invalid("invalid dust for buyvin"); // get user dest cc and normal addresses: - else if(GetAssetorigaddrs(cpAssets, origCCaddr_out, origaddr_out, vinTx) == false) + else if( GetAssetorigaddrs(cp, origCCaddr, origaddr, vinTx) == 0 ) return eval->Invalid("couldnt get origaddr for buyvin"); //fprintf(stderr,"AssetValidateCCvin() got %.8f to origaddr.(%s)\n", (double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr); + + if ( vinTx.vout[0].nValue == 0 ) + return eval->Invalid("null value CCvin"); - // check that vinTx B or S has assets cc vins: - if (vinFuncId == 'B' || vinFuncId == 'S') - { - bool found = false; - for (auto const &vin : vinTx.vin) - if (cpAssets->ismyvin(vin.scriptSig)) { - found = true; - break; - } - if (!found) - return eval->Invalid("no assets cc vins in previous fillbuy or fillsell tx"); - } - - // check no more other vins spending from global addr: - if (AssetsGetCCInputs(cpAssets, unspendableAddr, tx) != vinTx.vout[ASSETS_GLOBALADDR_VOUT].nValue) - return eval->Invalid("invalid assets cc vins found"); - - if (vinTx.vout[ASSETS_GLOBALADDR_VOUT].nValue == 0) - return eval->Invalid("null value in previous tx CC vout0"); - - return(vinTx.vout[ASSETS_GLOBALADDR_VOUT].nValue); + return(vinTx.vout[0].nValue); } -int64_t AssetValidateBuyvin(struct CCcontract_info *cpAssets, Eval* eval, int64_t &unit_price, std::vector &origpubkey_out, char *origCCaddr_out, char *origaddr_out, const CTransaction &tx, uint256 refassetid) +int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid) { - CTransaction vinTx; int64_t nValue; uint256 assetid, assetid2; uint8_t funcid, evalCode; - - origCCaddr_out[0] = origaddr_out[0] = 0; - // validate locked coins on Assets vin[1] - if ((nValue = AssetValidateCCvin(cpAssets, eval, origCCaddr_out, origaddr_out, tx, ASSETS_GLOBALADDR_VIN, vinTx)) == 0) - return(0); - else if (vinTx.vout.size() < 2) - return eval->Invalid("invalid previous tx, too few vouts"); - else if (vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == false) - return eval->Invalid("invalid not cc vout0 for buyvin"); - else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, unit_price, origpubkey_out)) == 'b' && - vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == false) // marker is only in 'b'? - return eval->Invalid("invalid not cc vout1 for buyvin"); + CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid, evalCode; + + CCaddr[0] = origaddr[0] = 0; + + // validate locked coins on Assets vin[1] + if ( (nValue= AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 ) + return(0); + else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) + return eval->Invalid("invalid normal vout0 for buyvin"); + else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey)) == 'b' && + vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) // marker is only in 'b'? + return eval->Invalid("invalid normal vout1 for buyvin"); else { //fprintf(stderr,"have %.8f checking assetid origaddr.(%s)\n",(double)nValue/COIN,origaddr); - if (vinTx.vout.size() > 0 && funcid != 'b' && funcid != 'B') + if ( vinTx.vout.size() > 0 && funcid != 'b' && funcid != 'B' ) return eval->Invalid("invalid opreturn for buyvin"); - else if (refassetid != assetid) + else if ( refassetid != assetid ) return eval->Invalid("invalid assetid for buyvin"); //int32_t i; for (i=31; i>=0; i--) // fprintf(stderr,"%02x",((uint8_t *)&assetid)[i]); @@ -548,35 +497,51 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cpAssets, Eval* eval, int64_ return(nValue); } -int64_t AssetValidateSellvin(struct CCcontract_info *cpAssets, Eval* eval, int64_t &unit_price, std::vector &origpubkey_out, char *origCCaddr_out, char *origaddr_out, const CTransaction &tx, uint256 assetid) +int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid) { - CTransaction vinTx; int64_t nValue, assetoshis; - + CTransaction vinTx; int64_t nValue,assetoshis; //fprintf(stderr,"AssetValidateSellvin()\n"); - if ((nValue = AssetValidateCCvin(cpAssets, eval, origCCaddr_out, origaddr_out, tx, ASSETS_GLOBALADDR_VIN, vinTx)) == 0) + if ( (nValue = AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 ) return(0); - - if ((assetoshis = IsAssetvout(cpAssets, unit_price, origpubkey_out, vinTx, ASSETS_GLOBALADDR_VOUT, assetid)) == 0) + if ( (assetoshis= IsAssetvout(cp, tmpprice, tmporigpubkey, vinTx, 0, assetid)) == 0 ) return eval->Invalid("invalid missing CC vout0 for sellvin"); - else - return(assetoshis); + else + return(assetoshis); } // validates opret for asset tx: -bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &remaining_units_out, std::vector &origpubkey_out) -{ +bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &price, std::vector &origpubkey) { + uint256 assetidOpret, assetidOpret2; uint8_t funcid, evalCode; // this is just for log messages indentation fur debugging recursive calls: int32_t n = tx.vout.size(); - if ((funcid = DecodeAssetTokenOpRet(tx.vout.back().scriptPubKey, evalCode, assetidOpret, assetidOpret2, remaining_units_out, origpubkey_out)) == 0) + if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey)) == 0) { - LOGSTREAMFN(ccassets_log, CCLOG_DEBUG1, stream << "called DecodeAssetTokenOpRet returned funcId=0 for opret from txid=" << tx.GetHash().GetHex() << std::endl); + std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned funcId=0 for opret from txid=" << tx.GetHash().GetHex() << std::endl; return(false); } +/* it is now on token level: + else if (funcid == 'c') + { + if (assetid != zeroid && assetid == tx.GetHash() && v == 0) { + //std::cerr << "ValidateAssetOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl; + return(true); + } + } + else if (funcid == 't') // TODO: check if this new block does not influence IsAssetVout + { + //std::cerr << "ValidateAssetOpret() assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; + if (assetid != zeroid && assetid == assetidOpret) { + //std::cerr << "ValidateAssetOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl; + return(true); + } + } */ + //else if ((funcid == 'b' || funcid == 'B') && v == 0) // critical! 'b'/'B' vout0 is NOT asset + // return(false); else if (funcid != 'E') { if (assetid != zeroid && assetidOpret == assetid) @@ -592,157 +557,94 @@ bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &re else if (v == 2 && assetid != zeroid && assetidOpret2 == assetid) return(true); } + + //std::cerr << "ValidateAssetOpret() return false funcid=" << (char)funcid << " assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; return false; } // Checks if the vout is a really Asset CC vout -int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &remaining_units_out, std::vector &origpubkey_out, const CTransaction& tx, int32_t v, uint256 refassetid) +int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid) { - // just check boundaries: + + //std::cerr << "IsAssetvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for assetid=" << refassetid.GetHex() << std::endl; + int32_t n = tx.vout.size(); - if (v >= n - 1) { - LOGSTREAMFN(ccassets_log, CCLOG_ERROR, stream << "internal err: (v >= n - 1), returning 0" << std::endl); - return -1; + // just check boundaries: + if (v >= n - 1) { // just moved this up (dimxy) + std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl; + return(0); } - if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition()) + if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here { - if (!ValidateAssetOpret(tx, v, refassetid, remaining_units_out, origpubkey_out)) { - LOGSTREAMFN(ccassets_log, CCLOG_ERROR, stream << "invalid assets opreturn" << std::endl); - return -1; - } - return tx.vout[v].nValue; + // moved opret checking to this new reusable func (dimxy): + const bool valOpret = ValidateAssetOpret(tx, v, refassetid, price, origpubkey); + //std::cerr << "IsAssetvout() ValidateAssetOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl; + if (valOpret) { + //std::cerr << "IsAssetvout() ValidateAssetOpret returned true, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl; + return tx.vout[v].nValue; + } + + //fprintf(stderr,"IsAssetvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str()); } + //fprintf(stderr,"IsAssetvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN); return(0); } // sets cc inputs vs cc outputs and ensures they are equal: -/*bool AssetsGetCCInputs(struct CCcontract_info *cpAssets, CAmount &tokensInputs, int64_t &assetsInputs, Eval* eval, const CTransaction &tx, uint256 assetid) +bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid) { - CTransaction vinTx; - uint256 hashBlock; + CTransaction vinTx; uint256 hashBlock, id, id2; int32_t flag; int64_t assetoshis; std::vector tmporigpubkey; int64_t tmpprice; int32_t numvins = tx.vin.size(); int32_t numvouts = tx.vout.size(); - tokensInputs = assetsInputs = 0; + inputs = outputs = 0; struct CCcontract_info *cpTokens, C; + cpTokens = CCinit(&C, EVAL_TOKENS); for (int32_t i = 0; iismyvin(tx.vin[i].scriptSig) || cpAssets->ismyvin(tx.vin[i].scriptSig)) + if (/*(*cpAssets->ismyvin)(tx.vin[i].scriptSig)*/ (*cpTokens->ismyvin)(tx.vin[i].scriptSig) ) // || IsVinAllowed(tx.vin[i].scriptSig) != 0) { - if (myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)) + //std::cerr << indentStr << "AssetExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl; + // we are not inside the validation code -- dimxy + if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock))) { - if (cpTokens->ismyvin(tx.vin[i].scriptSig)) - { - CAmount tokens = IsTokensvout(true, true, cpTokens, NULL, vinTx, tx.vin[i].prevout.n, assetid); - std::cerr << __func__ << " IsTokensvout tokens=" << tokens << " i=" << i << std::endl; - - if (tokens < 0) { - LOGSTREAMFN(ccassets_log, CCLOG_ERROR, stream << "invalid token input vintx for vin.i=" << i << " vin txid=" << tx.vin[i].prevout.hash.GetHex() << std::endl); - return (!eval) ? false : eval->Invalid("invalid tokens input"); - } - else if (tokens > 0) - { - LOGSTREAMFN(ccassets_log, CCLOG_DEBUG2, stream << "adding tokens amount=" << tokens << " for vin=" << i << std::endl); - tokensInputs += tokens; - } - } - else - { - CAmount dummy_remaining; - vuint8_t dummy_origpubkey; - CAmount assets = IsAssetvout(cpAssets, dummy_remaining, dummy_origpubkey, vinTx, tx.vin[i].prevout.n, assetid); - std::cerr << __func__ << " IsAssetvout assets=" << assets << " i=" << i << std::endl; - if (assets < 0) { - LOGSTREAMFN(ccassets_log, CCLOG_ERROR, stream << "invalid assets input vintx for vin.i=" << i << " vin txid=" << tx.vin[i].prevout.hash.GetHex() << std::endl); - return (!eval) ? false : eval->Invalid("invalid assets input"); - } - if (assets > 0) - { - LOGSTREAMFN(ccassets_log, CCLOG_DEBUG2, stream << "adding assets amount=" << assets << " for vin=" << i << std::endl); - assetsInputs += assets; - } - } - } - else { - LOGSTREAMFN(ccassets_log, CCLOG_ERROR, stream << "cannot load vintx for i." << i << " vin txid=" << tx.vin[i].prevout.hash.GetHex() << std::endl); + std::cerr << "AssetCalcAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl; return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt"); - } + } + else { + // validate vouts of vintx + //std::cerr << indentStr << "AssetExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl; + //assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, vinTx, tx.vin[i].prevout.n, assetid); + std::vector vopretExtra; + std::vector vinPubkeysEmpty; + + // TODO: maybe we do not need call to IsTokensVout here, cause we've already selected token vins + assetoshis = IsTokensvout(false, false, cpTokens, NULL, vinTx, tx.vin[i].prevout.n, assetid); + if (assetoshis != 0) + { + //std::cerr << "AssetCalcAmounts() vin i=" << i << " assetoshis=" << assetoshis << std::endl; + inputs += assetoshis; + } + } } } - return(true); -}*/ - -// get tx's vin inputs for cp and optional global addr -CAmount AssetsGetCCInputs(struct CCcontract_info *cp, const char *addr, const CTransaction &tx) -{ - CTransaction vinTx; - uint256 hashBlock; - CAmount inputs = 0LL; - //struct CCcontract_info *cpTokens, C; - //cpTokens = CCinit(&C, EVAL_TOKENS); - - for (int32_t i = 0; i < tx.vin.size(); i++) - { - if (cp->ismyvin(tx.vin[i].scriptSig)) + for (int32_t i = 0; i < numvouts-1; i++) + { + assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, tx, i, assetid); + if (assetoshis != 0) { - if (myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)) - { - char scriptaddr[KOMODO_ADDRESS_BUFSIZE]; - if (addr == NULL || Getscriptaddress(scriptaddr, vinTx.vout[tx.vin[i].prevout.n].scriptPubKey) && strcmp(scriptaddr, addr) == 0) { - std::cerr << __func__ << " adding amount=" << vinTx.vout[tx.vin[i].prevout.n].nValue << " for vin i=" << i << " eval=" << std::hex << (int)cp->evalcode << std::resetiosflags(std::ios::hex) << std::endl; - inputs += vinTx.vout[tx.vin[i].prevout.n].nValue; - } - } + //std::cerr << "AssetCalcAmounts() vout i=" << i << " assetoshis=" << assetoshis << std::endl; + outputs += assetoshis; } } - return inputs; -} - -/* -uint256 AssetsGetPrevOrdertxid(const CTransaction &tx) -{ - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_ASSETS); - for (int32_t i; i < tx.vin.size(); i ++) - if (cp->ismyvin(tx.vin[i].scriptSig)) - return tx.vin[i].prevout.hash; - return zeroid; -} + //std::cerr << "AssetCalcAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; -// get unit price from the initial order rx -CAmount AssetsGetUnitPrice(uint256 ordertxid) -{ - CTransaction ordertx; - uint256 hashBlock; - uint8_t funcid = 0; - - do { - if (myGetTransaction(ordertxid, ordertx, hashBlock)) - { - uint8_t evalCode; - uint256 assetid, assetid2; - int64_t amount; - std::vector origpubkey; - - funcid = DecodeAssetTokenOpRet(ordertx.vout.back().scriptPubKey, evalCode, assetid, assetid2, amount, origpubkey); - // price is defined by the first order tx; - if (funcid == 's') - return ordertx.vout[0].nValue > 0 ? amount / ordertx.vout[0].nValue : -1; - if (funcid == 'b') - return amount > 0 ? ordertx.vout[0].nValue / amount : -1; - } - else - return -1L; - ordertxid = AssetsGetPrevOrdertxid(ordertx); - } - while(funcid != 0 && ordertxid != zeroid); - - return 0L; + /* we do not verify inputs == outputs here, + it's now done in Tokens */ + return(true); } -*/ - diff --git a/src/cc/CCassetstx.cpp b/src/cc/CCassetstx.cpp index 216e4147242..654f01a383f 100644 --- a/src/cc/CCassetstx.cpp +++ b/src/cc/CCassetstx.cpp @@ -17,7 +17,7 @@ #include "CCtokens.h" -UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t evalCodeNFT) +UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t additionalEvalCode) { UniValue result(UniValue::VARR); @@ -30,27 +30,28 @@ UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t evalCodeNFT) auto addOrders = [&](struct CCcontract_info *cp, std::vector >::const_iterator it) { uint256 txid, hashBlock, assetid, assetid2; - int64_t unit_price; + int64_t price; std::vector origpubkey; CTransaction ordertx; uint8_t funcid, evalCode; - char numstr[32], funcidstr[16], origaddr[KOMODO_ADDRESS_BUFSIZE], origtokenaddr[KOMODO_ADDRESS_BUFSIZE]; + char numstr[32], funcidstr[16], origaddr[64], origtokenaddr[64]; txid = it->first.txhash; - LOGSTREAM(ccassets_log, CCLOG_DEBUG2, stream << "addOrders() checking txid=" << txid.GetHex() << std::endl); - if ( myGetTransaction(txid, ordertx, hashBlock) != 0) + LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() checking txid=" << txid.GetHex() << std::endl); + if ( myGetTransaction(txid, ordertx, hashBlock) != 0 ) { - if (ordertx.vout.size() > ASSETS_GLOBALADDR_VOUT && (funcid = DecodeAssetTokenOpRet(ordertx.vout.back().scriptPubKey, evalCode, assetid, assetid2, unit_price, origpubkey)) != 0) + // for logging: funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey); + if (ordertx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(ordertx.vout[ordertx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0) { - LOGSTREAM(ccassets_log, CCLOG_DEBUG2, stream << "addOrders() checking ordertx.vout.size()=" << ordertx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << " assetid=" << assetid.GetHex() << std::endl); + LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() checking ordertx.vout.size()=" << ordertx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << " assetid=" << assetid.GetHex() << std::endl); - if (!pk.IsValid() && (refassetid == zeroid || assetid == refassetid) || // tokenorders - pk.IsValid() && pk == pubkey2pk(origpubkey)) // mytokenorders + if (pk == CPubKey() && (refassetid == zeroid || assetid == refassetid) // tokenorders + || pk != CPubKey() && pk == pubkey2pk(origpubkey) && (funcid == 'S' || funcid == 's')) // mytokenorders, returns only asks (is this correct?) { - LOGSTREAM(ccassets_log, CCLOG_DEBUG2, stream << "addOrders() it->first.index=" << it->first.index << " ordertx.vout[it->first.index].nValue=" << ordertx.vout[it->first.index].nValue << std::endl); + LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() it->first.index=" << it->first.index << " ordertx.vout[it->first.index].nValue=" << ordertx.vout[it->first.index].nValue << std::endl); if (ordertx.vout[it->first.index].nValue == 0) { - LOGSTREAM(ccassets_log, CCLOG_DEBUG2, stream << "addOrders() order with value=0 skipped" << std::endl); + LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() order with value=0 skipped" << std::endl); return; } @@ -70,9 +71,9 @@ UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t evalCodeNFT) } else { - sprintf(numstr, "%lld", (long long)ordertx.vout[it->first.index].nValue); + sprintf(numstr, "%llu", (long long)ordertx.vout[it->first.index].nValue); item.push_back(Pair("amount", numstr)); - sprintf(numstr, "%lld", (long long)ordertx.vout[0].nValue); + sprintf(numstr, "%llu", (long long)ordertx.vout[0].nValue); item.push_back(Pair("askamount", numstr)); } if (origpubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) @@ -86,44 +87,44 @@ UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t evalCodeNFT) item.push_back(Pair("tokenid", assetid.GetHex())); if (assetid2 != zeroid) item.push_back(Pair("otherid", assetid2.GetHex())); - if (unit_price > 0) + if (price > 0) { - if (funcid == 's' || funcid == 'S' || funcid == 'e' || funcid == 'E') + if (funcid == 's' || funcid == 'S' || funcid == 'e' || funcid == 'e') { - sprintf(numstr, "%.8f", (double)unit_price * ordertx.vout[ASSETS_GLOBALADDR_VOUT].nValue / COIN); + sprintf(numstr, "%.8f", (double)price / COIN); item.push_back(Pair("totalrequired", numstr)); - //sprintf(numstr, "%.8f", (double)remaining_units / (COIN * ordertx.vout[0].nValue)); - item.push_back(Pair("price", ValueFromAmount(unit_price))); + sprintf(numstr, "%.8f", (double)price / (COIN * ordertx.vout[0].nValue)); + item.push_back(Pair("price", numstr)); } else { - item.push_back(Pair("totalrequired", unit_price ? (int64_t)ordertx.vout[ASSETS_GLOBALADDR_VOUT].nValue / unit_price : 0)); - //sprintf(numstr, "%.8f", (double)ordertx.vout[0].nValue / (remaining_units * COIN)); - item.push_back(Pair("price", ValueFromAmount(unit_price))); + item.push_back(Pair("totalrequired", (int64_t)price)); + sprintf(numstr, "%.8f", (double)ordertx.vout[0].nValue / (price * COIN)); + item.push_back(Pair("price", numstr)); } } result.push_back(item); - LOGSTREAM(ccassets_log, CCLOG_DEBUG1, stream << "addOrders() added order funcId=" << (char)(funcid ? funcid : ' ') << " it->first.index=" << it->first.index << " ordertx.vout[it->first.index].nValue=" << ordertx.vout[it->first.index].nValue << " tokenid=" << assetid.GetHex() << std::endl); + LOGSTREAM("ccassets", CCLOG_DEBUG1, stream << "addOrders() added order funcId=" << (char)(funcid ? funcid : ' ') << " it->first.index=" << it->first.index << " ordertx.vout[it->first.index].nValue=" << ordertx.vout[it->first.index].nValue << " tokenid=" << assetid.GetHex() << std::endl); } } } }; - std::vector > unspentOutputsTokens, unspentOutputsNFTs, unspentOutputsCoins; + std::vector > unspentOutputsTokens, unspentOutputsDualEvalTokens, unspentOutputsCoins; - char assetsUnspendableAddr[KOMODO_ADDRESS_BUFSIZE]; + char assetsUnspendableAddr[64]; GetCCaddress(cpAssets, assetsUnspendableAddr, GetUnspendable(cpAssets, NULL)); - SetCCunspents(unspentOutputsCoins, assetsUnspendableAddr, true); + SetCCunspents(unspentOutputsCoins, assetsUnspendableAddr,true); - char assetsTokensUnspendableAddr[KOMODO_ADDRESS_BUFSIZE]; - std::vector vopretNFT; + char assetsTokensUnspendableAddr[64]; + std::vector vopretNonfungible; if (refassetid != zeroid) { - GetNonfungibleData(refassetid, vopretNFT); - if (vopretNFT.size() > 0) - cpAssets->evalcodeNFT = vopretNFT.begin()[0]; + GetNonfungibleData(refassetid, vopretNonfungible); + if (vopretNonfungible.size() > 0) + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; } GetTokensCCaddress(cpAssets, assetsTokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); - SetCCunspents(unspentOutputsTokens, assetsTokensUnspendableAddr, true); + SetCCunspents(unspentOutputsTokens, assetsTokensUnspendableAddr,true); // tokenbids: for (std::vector >::const_iterator itCoins = unspentOutputsCoins.begin(); @@ -137,139 +138,244 @@ UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t evalCodeNFT) itTokens++) addOrders(cpAssets, itTokens); - if (evalCodeNFT != 0) { //this would be mytokenorders - char assetsNFTUnspendableAddr[KOMODO_ADDRESS_BUFSIZE]; + if (additionalEvalCode != 0) { //this would be mytokenorders + char assetsDualEvalTokensUnspendableAddr[64]; - // try also dual eval tokenasks (and we do not need bids (why? bids are on assets global addr anyway.)): - cpAssets->evalcodeNFT = evalCodeNFT; - GetTokensCCaddress(cpAssets, assetsNFTUnspendableAddr, GetUnspendable(cpAssets, NULL)); - SetCCunspents(unspentOutputsNFTs, assetsNFTUnspendableAddr,true); + // try also dual eval tokenasks (and we do not need bids): + cpAssets->additionalTokensEvalcode2 = additionalEvalCode; + GetTokensCCaddress(cpAssets, assetsDualEvalTokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); + SetCCunspents(unspentOutputsDualEvalTokens, assetsDualEvalTokensUnspendableAddr,true); - for (std::vector >::const_iterator itNFTs = unspentOutputsNFTs.begin(); - itNFTs != unspentOutputsNFTs.end(); - itNFTs++) - addOrders(cpAssets, itNFTs); + for (std::vector >::const_iterator itDualEvalTokens = unspentOutputsDualEvalTokens.begin(); + itDualEvalTokens != unspentOutputsDualEvalTokens.end(); + itDualEvalTokens++) + addOrders(cpAssets, itDualEvalTokens); } return(result); } +// not used (use TokenCreate instead) +/* std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk; struct CCcontract_info *cp,C; + if ( assetsupply < 0 ) + { + fprintf(stderr,"negative assetsupply %lld\n",(long long)assetsupply); + return(""); + } + cp = CCinit(&C,EVAL_ASSETS); + if ( name.size() > 32 || description.size() > 4096 ) + { + fprintf(stderr,"name.%d or description.%d is too big\n",(int32_t)name.size(),(int32_t)description.size()); + return(""); + } + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,assetsupply+2*txfee,64) > 0 ) + { + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,assetsupply,mypk)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetCreateOpRet('c',Mypubkey(),name,description))); + } + return(""); +} */ + +// not used (use TokenTransfer instead) +/* std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector destpubkey,int64_t total) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk; uint64_t mask; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C; + if ( total < 0 ) + { + fprintf(stderr,"negative total %lld\n",(long long)total); + return(""); + } + cp = CCinit(&C,EVAL_ASSETS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) + { + //n = outputs.size(); + //if ( n == amounts.size() ) + //{ + // for (i=0; i 0 ) + { + + if (inputs < total) { //added dimxy + std::cerr << "AssetTransfer(): insufficient funds" << std::endl; + return (""); + } + if ( inputs > total ) + CCchange = (inputs - total); + //for (i=0; i destpubkey,int64_t total,int32_t evalcode) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C; + if ( total < 0 ) + { + fprintf(stderr,"negative total %lld\n",(long long)total); + return(""); + } + cp = CCinit(&C,EVAL_ASSETS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) + { + if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,total,60)) > 0 ) + { + if ( inputs > total ) + CCchange = (inputs - total); + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk)); + mtx.vout.push_back(MakeCC1vout(evalcode,total,pubkey2pk(destpubkey))); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey()))); + } else fprintf(stderr,"not enough CC asset inputs for %.8f\n",(double)total/COIN); + } + return(""); +} */ + // rpc tokenbid implementation, locks 'bidamount' coins for the 'pricetotal' of tokens -UniValue CreateBuyOffer(const CPubKey &mypk, int64_t txfee, int64_t bidamount, uint256 assetid, int64_t numtokens) +std::string CreateBuyOffer(int64_t txfee, int64_t bidamount, uint256 assetid, int64_t pricetotal) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk; struct CCcontract_info *cpAssets, C; uint256 hashBlock; CTransaction vintx; std::vector origpubkey; std::string name,description; int64_t inputs; - std::vector oprets; - if (bidamount < 0 || numtokens < 0) + std::cerr << "CreateBuyOffer() bidamount=" << bidamount << " numtokens(pricetotal)=" << pricetotal << std::endl; + + if (bidamount < 0 || pricetotal < 0) { - CCerror = "negative bidamount or numtokens"; + fprintf(stderr,"negative bidamount %lld, pricetotal %lld\n", (long long)bidamount, (long long)pricetotal); return(""); } - - // check if valid token if (myGetTransaction(assetid, vintx, hashBlock) == 0) { - CCerror = "could not find assetid\n"; + fprintf(stderr,"cant find assetid\n"); return(""); } - if (vintx.vout.size() == 0 || DecodeTokenCreateOpRetV1(vintx.vout.back().scriptPubKey, origpubkey, name, description, oprets) == 0) + if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, origpubkey, name, description) == 0) { - CCerror = "assetid isn't token creation txid\n"; + fprintf(stderr,"assetid isnt assetcreation txid\n"); return(""); } - cpAssets = CCinit(&C, EVAL_ASSETS); // NOTE: assets here! + cpAssets = CCinit(&C,EVAL_ASSETS); // NOTE: assets here! if (txfee == 0) txfee = 10000; - // use AddNormalinputsRemote to sign only with mypk - if ((inputs = AddNormalinputsRemote(mtx, mypk, bidamount+(txfee+ASSETS_MARKER_AMOUNT), 0x10000)) > 0) + mypk = pubkey2pk(Mypubkey()); + + if ((inputs = AddNormalinputs(mtx, mypk, bidamount+(2*txfee), 64)) > 0) { + std::cerr << "CreateBuyOffer() inputs=" << inputs << std::endl; if (inputs < bidamount+txfee) { + std::cerr << "CreateBuyOffer(): insufficient coins to make buy offer" << std::endl; CCerror = strprintf("insufficient coins to make buy offer"); return (""); } - CAmount unit_price = bidamount / numtokens; - CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, 0); mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendableAssetsPubkey)); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, ASSETS_MARKER_AMOUNT, mypk)); - - UniValue sigData = FinalizeCCTxExt(IsRemoteRPCCall(), 0, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRetV1(assetid, {}, // TODO: actually this tx is not 'tokens', maybe it is better not to have token opret here but only asset opret. - { EncodeAssetOpRet('b', zeroid, unit_price, vuint8_t(mypk.begin(), mypk.end())) } )); // But still such token opret should not make problems because no token eval in these vouts - if (!ResultHasTx(sigData)) - return MakeResultError("Could not finalize tx"); - return sigData; - + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); + std::vector voutTokenPubkeys; // should be empty - no token vouts + + return FinalizeCCTx(0, cpAssets, mtx, mypk, txfee, + EncodeTokenOpRet(assetid, voutTokenPubkeys, // TODO: actually this tx is not 'tokens', maybe it is better not to have token opret here but only asset opret. + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('b', zeroid, pricetotal, Mypubkey())))); // But still such token opret should not make problems because no token eval in these vouts } - CCerror = "no coins found to make buy offer"; + CCerror = strprintf("no coins found to make buy offer"); return(""); } -// rpc tokenask implementation, locks 'numtokens' tokens for the 'askamount' -UniValue CreateSell(const CPubKey &mypk, int64_t txfee, int64_t numtokens, uint256 assetid, int64_t askamount) +// rpc tokenask implementation, locks 'askamount' tokens for the 'pricetotal' +std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t pricetotal) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk; uint64_t mask; int64_t inputs, CCchange; struct CCcontract_info *cpAssets, assetsC; struct CCcontract_info *cpTokens, tokensC; - if (numtokens < 0 || askamount < 0) { - CCerror = "negative askamount or numtokens"; + //std::cerr << "CreateSell() askamount=" << askamount << " pricetotal=" << pricetotal << std::endl; + + if (askamount < 0 || pricetotal < 0) { + fprintf(stderr,"negative askamount %lld, askamount %lld\n",(long long)pricetotal,(long long)askamount); return(""); } cpAssets = CCinit(&assetsC, EVAL_ASSETS); // NOTE: for signing + if (txfee == 0) txfee = 10000; - if (AddNormalinputsRemote(mtx, mypk, txfee+ASSETS_MARKER_AMOUNT, 0x10000) > 0) // use AddNormalinputsRemote to sign with mypk + mypk = pubkey2pk(Mypubkey()); + if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) { + std::vector vopretNonfungible; mask = ~((1LL << mtx.vin.size()) - 1); // add single-eval tokens (or non-fungible tokens): cpTokens = CCinit(&tokensC, EVAL_TOKENS); // NOTE: adding inputs only from EVAL_TOKENS cc - if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, numtokens, 60)) > 0) + if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, askamount, 60, vopretNonfungible)) > 0) { - if (inputs < numtokens) { + if (inputs < askamount) { + //was: askamount = inputs; + std::cerr << "CreateSell(): insufficient tokens for ask" << std::endl; CCerror = strprintf("insufficient tokens for ask"); return (""); } - CAmount unit_price = askamount / numtokens; - - uint8_t evalcodeNFT = cpTokens->evalcodeNFT ? cpTokens->evalcodeNFT : 0; + // if this is non-fungible tokens: + if( !vopretNonfungible.empty() ) + // set its evalcode + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, NULL); - mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, evalcodeNFT, numtokens, unspendableAssetsPubkey)); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, ASSETS_MARKER_AMOUNT, mypk)); //marker (seems, it is not for my tokenorders, not used yet) - if (inputs > numtokens) - CCchange = (inputs - numtokens); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, cpAssets->additionalTokensEvalcode2, askamount, unspendableAssetsPubkey)); + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); //marker (seems, it is not for tokenorders) + if (inputs > askamount) + CCchange = (inputs - askamount); if (CCchange != 0) // change to single-eval or non-fungible token vout (although for non-fungible token change currently is not possible) - mtx.vout.push_back(MakeTokensCC1vout(evalcodeNFT ? evalcodeNFT : EVAL_TOKENS, CCchange, mypk)); - - UniValue sigData = FinalizeCCTxExt(IsRemoteRPCCall(), mask, cpTokens, mtx, mypk, txfee, - EncodeTokenOpRetV1(assetid, { unspendableAssetsPubkey }, - { EncodeAssetOpRet('s', zeroid, unit_price, vuint8_t(mypk.begin(), mypk.end()) ) } )); - if (!ResultHasTx(sigData)) - return MakeResultError("Could not finalize tx"); - return sigData; + mtx.vout.push_back(MakeTokensCC1vout((cpAssets->additionalTokensEvalcode2) ? cpAssets->additionalTokensEvalcode2 : EVAL_TOKENS, CCchange, mypk)); + + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(unspendableAssetsPubkey); + + return FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, + EncodeTokenOpRet(assetid, voutTokenPubkeys, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey())))); } else { - CCerror = "need some tokens to place ask"; + fprintf(stderr, "need some tokens to place ask\n"); } } - else { - CCerror = "need some native coins to place ask"; + else { // dimxy added 'else', because it was misleading message before + fprintf(stderr, "need some native coins to place ask\n"); } return(""); } @@ -297,7 +403,7 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// mypk = pubkey2pk(Mypubkey()); - if (AddNormalinputs(mtx, mypk, txfee, 0x10000) > 0) + if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) { mask = ~((1LL << mtx.vin.size()) - 1); /*if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0) @@ -343,149 +449,137 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a return(""); } ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// -// unlocks coins, ends bid order -UniValue CancelBuyOffer(const CPubKey &mypk, int64_t txfee,uint256 assetid,uint256 bidtxid) +// unlocks coins +std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction vintx; uint64_t mask; uint256 hashBlock; int64_t bidamount; - struct CCcontract_info *cpAssets, C; + CPubKey mypk; struct CCcontract_info *cpAssets, C; + uint8_t funcid,dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector dummyOrigpubkey; cpAssets = CCinit(&C, EVAL_ASSETS); if (txfee == 0) txfee = 10000; - // add normal inputs only from my mypk (not from any pk in the wallet) to validate the ownership of the canceller - if (AddNormalinputsRemote(mtx, mypk, txfee+ASSETS_MARKER_AMOUNT, 0x10000) > 0) + mypk = pubkey2pk(Mypubkey()); + + if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) { mask = ~((1LL << mtx.vin.size()) - 1); - if (myGetTransaction(bidtxid, vintx, hashBlock) && vintx.vout.size() > ASSETS_GLOBALADDR_VOUT) + if (myGetTransaction(bidtxid, vintx, hashBlock) != 0) { - uint8_t dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector dummyOrigpubkey; - - //std::vector vopretNonfungible; - //GetNonfungibleData(assetid, vopretNonfungible); - - bidamount = vintx.vout[ASSETS_GLOBALADDR_VOUT].nValue; - mtx.vin.push_back(CTxIn(bidtxid, ASSETS_GLOBALADDR_VOUT, CScript())); // coins in Assets - - uint8_t funcid = DecodeAssetTokenOpRet(vintx.vout.back().scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey); - if (funcid == 'b' && vintx.vout.size() > 1) - mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b' - else if (funcid == 'B' && vintx.vout.size() > 3) - mtx.vin.push_back(CTxIn(bidtxid, 3, CScript())); // spend marker if funcid='B' - else { - CCerror = "invalid bidtx or not enough vouts"; - return ""; - } + std::vector vopretNonfungible; + GetNonfungibleData(assetid, vopretNonfungible); - mtx.vout.push_back(CTxOut(bidamount, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(CTxOut(ASSETS_MARKER_AMOUNT, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + bidamount = vintx.vout[0].nValue; + mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); // coins in Assets - UniValue sigData = FinalizeCCTxExt(IsRemoteRPCCall(), mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRetV1(assetid, {}, - { EncodeAssetOpRet('o', zeroid, 0, vuint8_t(mypk.begin(), mypk.end())) })); - if (!ResultHasTx(sigData)) - return MakeResultError("Could not finalize tx"); - return sigData; + if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) + { + if (funcid == 's') mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b' + else if (funcid=='S') mtx.vin.push_back(CTxIn(bidtxid, 3, CScript())); // spend marker if funcid='B' + } + + mtx.vout.push_back(CTxOut(bidamount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + + std::vector voutTokenPubkeys; // should be empty, no token vouts + + return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, + EncodeTokenOpRet(assetid, voutTokenPubkeys, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('o', zeroid, 0, Mypubkey()))))); } - else - CCerror = "could not load bid tx"; } - else - CCerror = "could not get normal coins for txfee"; return(""); } -//unlocks tokens, ends ask order -UniValue CancelSell(const CPubKey &mypk, int64_t txfee,uint256 assetid,uint256 asktxid) +//unlocks tokens +std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction vintx; uint64_t mask; uint256 hashBlock; int64_t askamount; + CPubKey mypk; struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC; + uint8_t funcid, dummyEvalCode; + uint256 dummyAssetid, dummyAssetid2; + int64_t dummyPrice; + std::vector dummyOrigpubkey; cpAssets = CCinit(&assetsC, EVAL_ASSETS); if (txfee == 0) txfee = 10000; - // add normal inputs only from my mypk (not from any pk in the wallet) to validate the ownership - if (AddNormalinputsRemote(mtx, mypk, txfee+ASSETS_MARKER_AMOUNT, 0x10000) > 0) + mypk = pubkey2pk(Mypubkey()); + + if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) { mask = ~((1LL << mtx.vin.size()) - 1); - if (myGetTransaction(asktxid, vintx, hashBlock) != 0 && vintx.vout.size() > 0) + if (myGetTransaction(asktxid, vintx, hashBlock) != 0) { - uint8_t dummyEvalCode; - uint256 dummyAssetid, dummyAssetid2; - int64_t dummyPrice; - std::vector dummyOrigpubkey; + std::vector vopretNonfungible; + GetNonfungibleData(assetid, vopretNonfungible); - askamount = vintx.vout[ASSETS_GLOBALADDR_VOUT].nValue; - mtx.vin.push_back(CTxIn(asktxid, ASSETS_GLOBALADDR_VOUT, CScript())); - - uint8_t funcid = DecodeAssetTokenOpRet(vintx.vout.back().scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey); - if (funcid == 's' && vintx.vout.size() > 1) - mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // spend marker if funcid='s' - else if (funcid == 'S' && vintx.vout.size() > 3) - mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // spend marker if funcid='S' - else { - CCerror = "invalid ask tx or not enough vouts"; - return ""; + askamount = vintx.vout[0].nValue; + mtx.vin.push_back(CTxIn(asktxid, 0, CScript())); + + if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) + { + if (funcid == 's') + mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s' + else if (funcid=='S') + mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // marker if funcid='S' } - std::vector vopretNonfungible; - GetNonfungibleData(assetid, vopretNonfungible); if (vopretNonfungible.size() > 0) - cpAssets->evalcodeNFT = vopretNonfungible.begin()[0]; + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; - mtx.vout.push_back(MakeTokensCC1vout(cpAssets->evalcodeNFT ? cpAssets->evalcodeNFT : EVAL_TOKENS, askamount, mypk)); // one-eval token vout - mtx.vout.push_back(CTxOut(ASSETS_MARKER_AMOUNT, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(MakeTokensCC1vout(cpAssets->additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : cpAssets->additionalTokensEvalcode2, askamount, mypk)); // one-eval token vout + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(mypk); - // this is only for unspendable addresses: + // this is only for unspendable addresses: //CCaddr2set(cpTokens, EVAL_ASSETS, mypk, myPrivkey, myCCaddr); //do we need this? Seems FinalizeCCTx can attach to any evalcode cc addr by calling Getscriptaddress - uint8_t unspendableAssetsPrivkey[32]; - char unspendableAssetsAddr[KOMODO_ADDRESS_BUFSIZE]; - // init assets 'unspendable' privkey and pubkey - CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); - GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk); - - // add additional eval-tokens unspendable assets privkey: - CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); - - UniValue sigData = FinalizeCCTxExt(IsRemoteRPCCall(), mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRetV1(assetid, { mypk }, - { EncodeAssetOpRet('x', zeroid, 0, vuint8_t(mypk.begin(), mypk.end())) } )); - if (!ResultHasTx(sigData)) - return MakeResultError("Could not finalize tx"); - return sigData; + uint8_t unspendableAssetsPrivkey[32]; + char unspendableAssetsAddr[64]; + // init assets 'unspendable' privkey and pubkey + CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); + GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk); + + // add additional eval-tokens unspendable assets privkey: + CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); + + return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, + EncodeTokenOpRet(assetid, voutTokenPubkeys, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('x', zeroid, 0, Mypubkey()))))); } - else - CCerror = "could not get ask tx"; } - else - CCerror = "could not get normal coins for txfee"; return(""); } //send tokens, receive coins: -UniValue FillBuyOffer(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 bidtxid, int64_t fill_units, CAmount paid_unit_price) +std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t fillamount) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction vintx; uint256 hashBlock; + CPubKey mypk; std::vector origpubkey; - int32_t bidvout = ASSETS_GLOBALADDR_VOUT; + int32_t bidvout=0; uint64_t mask; - int64_t orig_units, unit_price, bid_amount, paid_amount, remaining_units, inputs, CCchange=0; + int64_t origprice, bidamount, paid_amount, remaining_required, inputs, CCchange=0; struct CCcontract_info *cpTokens, tokensC; struct CCcontract_info *cpAssets, assetsC; - if (fill_units < 0) + if (fillamount < 0) { - CCerror = "negative fill units"; + fprintf(stderr,"negative fillamount %lld\n", (long long)fillamount); return(""); } cpTokens = CCinit(&tokensC, EVAL_TOKENS); @@ -493,212 +587,194 @@ UniValue FillBuyOffer(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint2 if (txfee == 0) txfee = 10000; - if (AddNormalinputs(mtx, mypk, txfee+ASSETS_MARKER_AMOUNT, 0x10000, IsRemoteRPCCall()) > 0) + mypk = pubkey2pk(Mypubkey()); + + if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) { mask = ~((1LL << mtx.vin.size()) - 1); - if (myGetTransaction(bidtxid, vintx, hashBlock) != 0 && vintx.vout.size() > bidvout) + if (myGetTransaction(bidtxid, vintx, hashBlock) != 0) { - bid_amount = vintx.vout[bidvout].nValue; - uint8_t funcid = SetAssetOrigpubkey(origpubkey, unit_price, vintx); // get orig pk, orig units - if (funcid != 'b' && funcid != 'B') { - CCerror = "not an bid order"; - return ""; - } - orig_units = bid_amount / unit_price; - if (paid_unit_price == 0) - paid_unit_price = unit_price; - mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); // Coins on Assets unspendable - - if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fill_units, 60)) > 0) + bidamount = vintx.vout[bidvout].nValue; + SetAssetOrigpubkey(origpubkey, origprice, vintx); + + mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); // Coins on Assets unspendable + + std::vector vopretNonfungible; + if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60, vopretNonfungible)) > 0) { - if (inputs < fill_units) { - CCerror = strprintf("insufficient tokens to fill buy offer"); - return (""); - } - - SetBidFillamounts(unit_price, paid_amount, bid_amount, fill_units, orig_units, paid_unit_price); - - if (inputs > fill_units) - CCchange = (inputs - fill_units); - - uint8_t unspendableAssetsPrivkey[32]; - cpAssets = CCinit(&assetsC, EVAL_ASSETS); - CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); - - uint8_t evalcodeNFT = cpTokens->evalcodeNFT ? cpTokens->evalcodeNFT : 0; + if (inputs < fillamount) { + std::cerr << "FillBuyOffer(): insufficient tokens to fill buy offer" << std::endl; + CCerror = strprintf("insufficient tokens to fill buy offer"); + return (""); + } + + SetBidFillamounts(paid_amount, remaining_required, bidamount, fillamount, origprice); - if (orig_units - fill_units > 0) - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bid_amount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder - else - mtx.vout.push_back(CTxOut(bid_amount - paid_amount, CScript() << ParseHex(HexStr(origpubkey)) << OP_CHECKSIG)); // if no more tokens to buy, send the remainder to originator - mtx.vout.push_back(CTxOut(paid_amount, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // vout1 coins to mypk normal - mtx.vout.push_back(MakeTokensCC1vout(evalcodeNFT ? evalcodeNFT : EVAL_TOKENS, fill_units, pubkey2pk(origpubkey))); // vout2 single-eval tokens sent to the originator - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, ASSETS_MARKER_AMOUNT, origpubkey)); // vout3 marker to origpubkey + uint8_t additionalTokensEvalcode2 = 0; + if (vopretNonfungible.size() > 0) + additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + + if (inputs > fillamount) + CCchange = (inputs - fillamount); + + uint8_t unspendableAssetsPrivkey[32]; + cpAssets = CCinit(&assetsC, EVAL_ASSETS); + CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); + + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder + mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // vout1 coins to normal + mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, fillamount, pubkey2pk(origpubkey))); // vout2 single-eval tokens sent to the originator + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, origpubkey)); // vout3 marker to origpubkey + + if (CCchange != 0) + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // vout4 change in single-eval tokens - if (CCchange != 0) - mtx.vout.push_back(MakeTokensCC1vout(evalcodeNFT ? evalcodeNFT : EVAL_TOKENS, CCchange, mypk)); // vout4 change in single-eval tokens + fprintf(stderr,"FillBuyOffer() remaining %llu -> origpubkey\n", (long long)remaining_required); - //fprintf(stderr, "%s remaining_units %lld -> origpubkey\n", __func__, (long long)remaining_units); + char unspendableAssetsAddr[64]; + cpAssets = CCinit(&assetsC, EVAL_ASSETS); + GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk); - char unspendableAssetsAddr[KOMODO_ADDRESS_BUFSIZE]; - cpAssets = CCinit(&assetsC, EVAL_ASSETS); - GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk); + // add additional unspendable addr from Assets: + CCaddr2set(cpTokens, EVAL_ASSETS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); - // add additional unspendable addr from Assets: - CCaddr2set(cpTokens, EVAL_ASSETS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); + // token vout verification pubkeys: + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(pubkey2pk(origpubkey)); - UniValue sigData = FinalizeCCTxExt(IsRemoteRPCCall(), mask, cpTokens, mtx, mypk, txfee, - EncodeTokenOpRetV1(assetid, { pubkey2pk(origpubkey) }, - { EncodeAssetOpRet('B', zeroid, unit_price, origpubkey) })); - if (!ResultHasTx(sigData)) - return MakeResultError("Could not finalize tx"); - return sigData; - } - else { - CCerror = "dont have any assets to fill bid"; - return ""; - } - } - else { - CCerror = "can't load or bad bidtx"; - return ""; + return(FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, + EncodeTokenOpRet(assetid, voutTokenPubkeys, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey))))); + } else return("dont have any assets to fill bid"); } } - CCerror = "no normal coins left"; - return ""; + return("no normal coins left"); } // send coins, receive tokens -UniValue FillSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 assetid2, uint256 asktxid, int64_t fillunits, CAmount paid_unit_price) +std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 asktxid, int64_t fillunits) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction vintx; + CTransaction vintx,filltx; uint256 hashBlock; + CPubKey mypk; std::vector origpubkey; - //double dprice; + double dprice; uint64_t mask = 0; - int32_t askvout = ASSETS_GLOBALADDR_VOUT; - int64_t unit_price, orig_assetoshis, paid_nValue, inputs, CCchange = 0LL; + int32_t askvout = 0; + int64_t received_assetoshis, total_nValue, orig_assetoshis, paid_nValue, remaining_nValue, inputs, CCchange=0; + //struct CCcontract_info *cpTokens, tokensC; struct CCcontract_info *cpAssets, assetsC; if (fillunits < 0) { CCerror = strprintf("negative fillunits %lld\n",(long long)fillunits); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } if (assetid2 != zeroid) { CCerror = "asset swaps disabled"; + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } std::vector vopretNonfungible; - uint8_t evalCodeNFT = 0; + uint8_t additionalTokensEvalcode2 = 0; GetNonfungibleData(assetid, vopretNonfungible); if (vopretNonfungible.size() > 0) - evalCodeNFT = vopretNonfungible.begin()[0]; + additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; cpAssets = CCinit(&assetsC, EVAL_ASSETS); if (txfee == 0) txfee = 10000; - if (myGetTransaction(asktxid, vintx, hashBlock) != 0 && vintx.vout.size() > askvout) - { - orig_assetoshis = vintx.vout[askvout].nValue; - uint8_t funcid = SetAssetOrigpubkey(origpubkey, unit_price, vintx); // get orig pk, orig value - if (funcid != 's' && funcid != 'S') { - CCerror = "not an ask order"; - return ""; - } - - //dprice = (double)orig_nValue / orig_assetoshis; - if (paid_unit_price <= 0LL) - paid_unit_price = unit_price; - if (paid_unit_price <= 0LL) { - CCerror = "could not get unit price"; - return ""; - } - paid_nValue = paid_unit_price * fillunits; - - if (assetid2 != zeroid) { - inputs = 0; // = AddAssetInputs(cpAssets, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet - } - else - { - // Use only one AddNormalinputs() in each rpc call to allow payment if user has only single utxo with normal funds - inputs = AddNormalinputs(mtx, mypk, txfee + ASSETS_MARKER_AMOUNT + paid_nValue, 0x10000, IsRemoteRPCCall()); - mask = ~((1LL << mtx.vin.size()) - 1); - } - if (inputs > 0) + mypk = pubkey2pk(Mypubkey()); + //if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) + //{ + //mask = ~((1LL << mtx.vin.size()) - 1); + if (myGetTransaction(asktxid, vintx, hashBlock) != 0) { - if (inputs < paid_nValue) { - CCerror = strprintf("insufficient coins to fill sell"); - return (""); - } + orig_assetoshis = vintx.vout[askvout].nValue; + SetAssetOrigpubkey(origpubkey, total_nValue, vintx); + dprice = (double)total_nValue / orig_assetoshis; + paid_nValue = dprice * fillunits; - // cc vin should be after normal vin - mtx.vin.push_back(CTxIn(asktxid, askvout, CScript())); - - if (assetid2 != zeroid) - ; // SetSwapFillamounts(orig_unit_price, fillunits, orig_assetoshis, paid_nValue, orig_nValue); //not implemented correctly yet - else - SetAskFillamounts(unit_price, fillunits, orig_assetoshis, paid_nValue); - - if (paid_nValue == 0) { - CCerror = "ask totally filled"; - return ""; + if (assetid2 != zeroid) { + inputs = 0; // = AddAssetInputs(cpAssets, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet } - - if (assetid2 != zeroid && inputs > paid_nValue) - CCchange = (inputs - paid_nValue); - - // vout.0 tokens remainder to unspendable cc addr: - mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, evalCodeNFT, orig_assetoshis - fillunits, GetUnspendable(cpAssets, NULL))); // token remainder on cc global addr - //vout.1 purchased tokens to self token single-eval or dual-eval token+nonfungible cc addr: - mtx.vout.push_back(MakeTokensCC1vout(evalCodeNFT ? evalCodeNFT : EVAL_TOKENS, fillunits, mypk)); + else + { + inputs = AddNormalinputs(mtx, mypk, 2 * txfee + paid_nValue, 60); // Better to use single AddNormalinputs() to allow payment if user has only single utxo with normal funds + mask = ~((1LL << mtx.vin.size()) - 1); + } + if (inputs > 0) + { + if (inputs < paid_nValue) { + std::cerr << "FillSell(): insufficient coins to fill sell" << std::endl; + CCerror = strprintf("insufficient coins to fill sell"); + return (""); + } + + // cc vin should be after normal vin + mtx.vin.push_back(CTxIn(asktxid, askvout, CScript())); + + if (assetid2 != zeroid) + SetSwapFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); //not implemented correctly yet + else + SetAskFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); + + if (assetid2 != zeroid && inputs > paid_nValue) + CCchange = (inputs - paid_nValue); + + // vout.0 tokens remainder to unspendable cc addr: + mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, additionalTokensEvalcode2, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL))); + //vout.1 purchased tokens to self token single-eval or dual-eval token+nonfungible cc addr: + mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, received_assetoshis, mypk)); - if (assetid2 != zeroid) { - std::cerr << __func__ << " WARNING: asset swap not implemented yet!" << std::endl; - // TODO: change MakeCC1vout appropriately when implementing: - //mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, paid_nValue, origpubkey)); //vout.2 tokens... (swap is not implemented yet) - } - else { - mtx.vout.push_back(CTxOut(paid_nValue, CScript() << origpubkey << OP_CHECKSIG)); //vout.2 coins to originator's normal addr - } - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, ASSETS_MARKER_AMOUNT, origpubkey)); //vout.3 marker to origpubkey (for my tokenorders?) + if (assetid2 != zeroid) { + std::cerr << "FillSell() WARNING: asset swap not implemented yet! (paid_nValue)" << std::endl; + // TODO: change MakeCC1vout appropriately when implementing: + //mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, paid_nValue, origpubkey)); //vout.2 tokens... (swap is not implemented yet) + } + else { + //std::cerr << "FillSell() paid_value=" << paid_nValue << " origpubkey=" << HexStr(pubkey2pk(origpubkey)) << std::endl; + mtx.vout.push_back(CTxOut(paid_nValue, CScript() << origpubkey << OP_CHECKSIG)); //vout.2 coins to tokens seller's normal addr + } + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,txfee,origpubkey)); //vout.3 marker to origpubkey - // not implemented - if (CCchange != 0) { - std::cerr << __func__ << " WARNING: asset swap not implemented yet! (CCchange)" << std::endl; - // TODO: change MakeCC1vout appropriately when implementing: - //mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); //vout.3 coins in Assets cc addr (swap not implemented) - } - - uint8_t unspendableAssetsPrivkey[32]; - char unspendableAssetsAddr[KOMODO_ADDRESS_BUFSIZE]; - // init assets 'unspendable' privkey and pubkey - CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); - GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk); - - // add additional eval-tokens unspendable assets privkey: - CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); - - cpAssets->evalcodeNFT = evalCodeNFT; // set nft eval for signing - - UniValue sigData = FinalizeCCTxExt(IsRemoteRPCCall(), mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRetV1(assetid, { mypk }, - { EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, unit_price, origpubkey) } )); - if (!ResultHasTx(sigData)) - return MakeResultError("Could not finalize tx"); - return sigData; - } else { - CCerror = strprintf("filltx not enough normal utxos"); - return ""; + // not implemented + if (CCchange != 0) { + std::cerr << "FillSell() WARNING: asset swap not implemented yet! (CCchange)" << std::endl; + // TODO: change MakeCC1vout appropriately when implementing: + //mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); //vout.3 coins in Assets cc addr (swap not implemented) + } + + uint8_t unspendableAssetsPrivkey[32]; + char unspendableAssetsAddr[64]; + // init assets 'unspendable' privkey and pubkey + CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); + GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk); + + // add additional eval-tokens unspendable assets privkey: + CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); + + // vout verification pubkeys: + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(mypk); + + cpAssets->additionalTokensEvalcode2 = additionalTokensEvalcode2; + + return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, + EncodeTokenOpRet(assetid, voutTokenPubkeys, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey))))); + } else { + CCerror = strprintf("filltx not enough utxos"); + fprintf(stderr,"%s\n", CCerror.c_str()); + } } - } - CCerror = "can't get ask tx"; + //} return(""); } - diff --git a/src/cc/CCchannels.h b/src/cc/CCchannels.h index c6d6ae0782f..ecca145d39e 100644 --- a/src/cc/CCchannels.h +++ b/src/cc/CCchannels.h @@ -21,9 +21,8 @@ #define CHANNELS_MAXPAYMENTS 1000 bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); -UniValue ChannelOpen(const CPubKey& pk,uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment,uint16_t confirmation,uint256 tokenid); +UniValue ChannelOpen(const CPubKey& pk,uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 tokenid); UniValue ChannelPayment(const CPubKey& pk,uint64_t txfee,uint256 opentxid,int64_t amount, uint256 secret); -UniValue ChannelGenerateSecret(const CPubKey& pk,uint256 opentxid,int64_t amount); UniValue ChannelClose(const CPubKey& pk,uint64_t txfee,uint256 opentxid); UniValue ChannelRefund(const CPubKey& pk,uint64_t txfee,uint256 opentxid,uint256 closetxid); UniValue ChannelsList(const CPubKey& pk); diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp index 3fc46f63257..6c66b3599fd 100644 --- a/src/cc/CCcustom.cpp +++ b/src/cc/CCcustom.cpp @@ -32,7 +32,6 @@ #include "CCGateways.h" #include "CCtokens.h" #include "CCImportGateway.h" -#include "CCKogs.h" /* CCcustom has most of the functions that need to be extended to create a new CC contract. @@ -254,18 +253,6 @@ uint8_t ImportGatewayCCpriv[32] = { 0x65, 0xef, 0x27, 0xeb, 0x3d, 0xb0, 0xb4, 0x #undef FUNCNAME #undef EVALCODE -// Kogs -#define FUNCNAME IsKogsInput -#define EVALCODE EVAL_KOGS -const char *KogsCCaddr = "RD3UQofnS7uqa9Z3cKC8cb9c95VvoxnPyo"; -const char *KogsNormaladdr = "RVH1M8ZmT2nPB7MW6726RRsxjY7D5FKQHa"; -char KogsCChexstr[67] = { "03c27db737b92826d37fb43f3fda3d1b1d258cd28b68fe4be605457bf9dd9e0218" }; -uint8_t KogsCCpriv[32] = { 0x9f, 0x9a, 0x85, 0x6d, 0xd9, 0x2b, 0xfe, 0xcb, 0xa1, 0x18, 0xca, 0x51, 0x06, 0x80, 0x87, 0x7f, 0x87, 0xaa, 0xef, 0x9c, 0x6e, 0xa0, 0x21, 0x21, 0xed, 0x1c, 0x89, 0x96, 0xc6, 0xe6, 0x93, 0x21 }; -#include "CCcustom.inc" -#undef FUNCNAME -#undef EVALCODE - - int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode) { CPubKey pk; int32_t i; uint8_t pub33[33],check33[33],hash[32]; char CCaddr[64],checkaddr[64],str[67]; @@ -313,9 +300,8 @@ int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode) struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) { - // memset(cp, '\0', sizeof(*cp)); <-- it is not good to initialize objects like this. - // special init func now used: - cp->init_to_zeros(); + // important to clear because not all members are always initialized! + memset(cp, '\0', sizeof(*cp)); cp->evalcode = evalcode; switch ( evalcode ) @@ -457,15 +443,6 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) cp->validate = ImportGatewayValidate; cp->ismyvin = IsImportGatewayInput; break; - case EVAL_KOGS: - strcpy(cp->unspendableCCaddr, KogsCCaddr); - strcpy(cp->normaladdr, KogsNormaladdr); - strcpy(cp->CChexstr, KogsCChexstr); - memcpy(cp->CCpriv, KogsCCpriv, 32); - cp->validate = KogsValidate; - cp->ismyvin = IsKogsInput; - break; - default: if ( CClib_initcp(cp,evalcode) < 0 ) return(0); diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 46367a44ce1..1d28e2cc939 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -84,9 +84,6 @@ Details. #define dstr(x) ((double)(x) / SATOSHIDEN) #define CCDISABLEALL memset(ASSETCHAINS_CCDISABLES,1,sizeof(ASSETCHAINS_CCDISABLES)) #define CCENABLE(x) ASSETCHAINS_CCDISABLES[((uint8_t)x)] = 0 -#define bits256_nonz(a) (((a).ulongs[0] | (a).ulongs[1] | (a).ulongs[2] | (a).ulongs[3]) != 0) - -#define MAY2020_NNELECTION_HARDFORK 1592146800 //(Sunday, June 14th, 2020 03:00:00 PM UTC) /* moved to komodo_cJSON.h #ifndef _BITS256 @@ -97,21 +94,36 @@ Details. */ /// \endcond -typedef std::vector vscript_t; // for oprets -typedef std::vector vuint8_t; // for other types - - -// get cc module blob (non import or burn blob) from opreturn data -inline bool GetOpReturnCCBlob(const std::vector &oprets, std::vector &vopret) { - vopret.clear(); - for(const auto &o : oprets) if (o.size() > 0 && o[0] != EVAL_IMPORTCOIN) { vopret = o; return true; } - return false; -} +/// identifiers of additional data blobs in token opreturn script: +/// @see EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector> oprets) +/// @see GetOpretBlob +enum opretid : uint8_t { + // cc contracts data: + OPRETID_NONFUNGIBLEDATA = 0x11, //!< NFT data id + OPRETID_ASSETSDATA = 0x12, //!< assets contract data id + OPRETID_GATEWAYSDATA = 0x13, //!< gateways contract data id + OPRETID_CHANNELSDATA = 0x14, //!< channels contract data id + OPRETID_HEIRDATA = 0x15, //!< heir contract data id + OPRETID_ROGUEGAMEDATA = 0x16, //!< rogue contract data id + OPRETID_PEGSDATA = 0x17, //!< pegs contract data id + + /*! \cond INTERNAL */ + // non cc contract data: + OPRETID_FIRSTNONCCDATA = 0x80, + /*! \endcond */ + OPRETID_BURNDATA = 0x80, //!< burned token data id + OPRETID_IMPORTDATA = 0x81 //!< imported token data id +}; -/// get import or burn blob from opreturn data -inline bool GetOpReturnImportBlob(const std::vector &oprets, std::vector &vopret) { +/// finds opret blob data by opretid in the vector of oprets +/// @param oprets vector of oprets +/// @param id opret id to search +/// @param vopret found opret blob as byte array +/// @returns true if found +/// @see opretid +inline bool GetOpretBlob(const std::vector>> &oprets, uint8_t id, std::vector &vopret) { vopret.clear(); - for(const auto &o : oprets) if (o.size() > 0 && o[0] == EVAL_IMPORTCOIN) { vopret = o; return true; } + for(auto p : oprets) if (p.first == id) { vopret = p.second; return true; } return false; } @@ -137,58 +149,11 @@ struct CC_meta }; /// \endcond -// dimxy -// class CCWrapper encapsulates and stores cryptocondition encoded in json -// stored cc is used as probe in FinalizeCCtx to find vintx cc vout and make matching tx.vin.scriptSig -class CCwrapper { -public: - CCwrapper() { } - - void setCC(CC *cond) { - - if (cond) - { - char *jsonstr = cc_conditionToJSONString(cond); - if (jsonstr) { - ccJsonString = jsonstr; - free(jsonstr); - } - //std::cerr << "CCwrapper setCC setting ccJsonString" << std::endl; - } - } - - CC *getCC() { - char err[1024] = ""; - CC *cond = NULL; - - if (!ccJsonString.empty()) - cond = cc_conditionFromJSONString(ccJsonString.c_str(), err); // caller, please don't forget to cc_free the returned cond! - - // debug logging if parse not successful: - //std::cerr << "CCwrapper ccJsonString=" << ccJsonString << "\nerr=" << err << std::endl; - // if( cond ) std::cerr << "CCwrapper serialized=" << cc_conditionToJSONString(cond) << std::endl; //see how it is serialized back - return cond; - } - - ~CCwrapper() { } - -private: - std::string ccJsonString; -}; - -// struct with cc and privkey -// cc is used as a probe to detect vintx cc vouts in FinalizeCCtx -// CCVintxProbe object is passed to FinalizCCtx inside a vector of probe ccs -struct CCVintxProbe { - CCwrapper CCwrapped; - uint8_t CCpriv[32]; - ~CCVintxProbe() { memset(CCpriv, '\0', sizeof(CCpriv)); } -}; /// CC contract (Antara module) info structure that contains data used for signing and validation of cc contract transactions struct CCcontract_info { uint8_t evalcode; //!< cc contract eval code, set by CCinit function - uint8_t evalcodeNFT; //!< additional eval code for spending from three-eval-code vouts with EVAL_TOKENS, cc evalcode, cc evalcode NFT + uint8_t additionalTokensEvalcode2; //!< additional eval code for spending from three-eval-code vouts with EVAL_TOKENS, evalcode, additionalTokensEvalcode2 //!< or vouts with two evalcodes: EVAL_TOKENS, additionalTokensEvalcode2. //!< Set by AddTokenCCInputs function @@ -242,37 +207,6 @@ struct CCcontract_info /// @private uint8_t didinit; - - std::vector< struct CCVintxProbe > CCvintxprobes; // vscript_t; extern struct NSPV_CCmtxinfo NSPV_U; //!< global variable with info about mtx object and used utxo #ifdef ENABLE_WALLET @@ -311,7 +246,6 @@ bool GetAddressUnspent(uint160 addressHash, int type,std::vector &hexdata,cJSON *item,int32_t len); // int32_t komodo_blockload(CBlock& block,CBlockIndex *pindex); // this def in komodo_defs.h +/// Makes opreturn scriptPubKey for token creation transaction. Normally this function is called internally by the tokencreate rpc. You might need to call this function to create a customized token. +/// The total opreturn length should not exceed 10001 byte +/// @param funcid should be set to 'c' character +/// @param origpubkey token creator pubkey as byte array +/// @param name token name (no more than 32 char) +/// @param description token description (no more than 4096 char) +/// @param vopretNonfungible NFT data, could be empty. If not empty, NFT will be created, the first byte if the NFT data should be set to the eval code of the contract validating this NFT data +/// @returns scriptPubKey with OP_RETURN script +CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, vscript_t vopretNonfungible); + /// Makes opreturn scriptPubKey for token creation transaction. Normally this function is called internally by the tokencreate rpc. You might need to call it to create a customized token. /// The total opreturn length should not exceed 10001 byte /// @param funcid should be set to 'c' character /// @param origpubkey token creator pubkey as byte array /// @param name token name (no more than 32 char) /// @param description token description (no more than 4096 char) -/// @param oprets vector of additional data added to the token opret +/// @param oprets vector of pairs of additional data added to the token opret. The first element in the pair is opretid enum, the second is the data as byte array +/// @returns scriptPubKey with OP_RETURN script +/// @see opretid +CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector> oprets); + +/// Makes opreturn scriptPubKey for token transaction. Normally this function is called internally by the token rpcs. You might call this function if your module should create a customized token. +/// The total opreturn length should not exceed 10001 byte +/// @param tokenid id of the token +/// @param voutPubkeys vector of pubkeys used to make the token vout in the same transaction that the created opreturn is for, the pubkeys are used for token vout validation +/// @param opretWithId a pair of additional opreturn data added to the token opret. Could be empty. The first element in the pair is opretid enum, the second is the data as byte array /// @returns scriptPubKey with OP_RETURN script -CScript EncodeTokenCreateOpRetV1(const std::vector &origpubkey, const std::string &name, const std::string &description, const std::vector &oprets); +CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::pair opretWithId); /// An overload to make opreturn scriptPubKey for token transaction. Normally this function is called internally by the token rpcs. You might call this function if your module should create a customized token. /// The total opreturn length should not exceed 10001 byte @@ -449,16 +423,15 @@ CScript EncodeTokenCreateOpRetV1(const std::vector &origpubkey, const s /// @param voutPubkeys vector of pubkeys used to make the token vout in the same transaction that the created opreturn is for, the pubkeys are used for token vout validation /// @param oprets vector of pairs of additional opreturn data added to the token opret. Could be empty. The first element in the pair is opretid enum, the second is the data as byte array /// @returns scriptPubKey with OP_RETURN script -CScript EncodeTokenOpRetV1(uint256 tokenid, const std::vector &voutPubkeys, const std::vector &oprets); +CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::vector> oprets); -/// Overload that decodes opreturn scriptPubKey of token creation transaction and also returns additional data blobs. -/// Normally this function is called internally by the token rpcs. You might want to call this function if your module should create a customized token. +/// Decodes opreturn scriptPubKey of token creation transaction. Normally this function is called internally by the token rpcs. You might call this function if your module should create a customized token. /// @param scriptPubKey OP_RETURN script to decode /// @param[out] origpubkey creator public key as a byte array /// @param[out] name token name /// @param[out] description token description /// @returns funcid ('c') or NULL if errors -uint8_t DecodeTokenCreateOpRetV1(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description); +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description); /// Overload that decodes opreturn scriptPubKey of token creation transaction and also returns additional data blobs. /// Normally this function is called internally by the token rpcs. You might want to call this function if your module should create a customized token. @@ -466,9 +439,9 @@ uint8_t DecodeTokenCreateOpRetV1(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector &oprets); +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector> &oprets); /// Decodes opreturn scriptPubKey of token transaction, also returns additional data blobs. /// Normally this function is called internally by different token rpc. You might want to call if your module created a customized token. @@ -476,14 +449,21 @@ uint8_t DecodeTokenCreateOpRetV1(const CScript &scriptPubKey, std::vector &voutPubkeys, std::vector &oprets); - +uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector> &oprets); /// @private int64_t AddCClibtxfee(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk); +/// Returns non-fungible data of token if this is a NFT +/// @param tokenid id of token +/// @param vopretNonfungible non-fungible token data. The first byte is the evalcode of the contract that validates the NFT-data +void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible); + +/// @private +bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector &vinPubkeys); + // CCcustom /// Returns global pubkey for the evalcode and sets private key for the global pubkey @@ -597,6 +577,109 @@ void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t * /// @see GetCCaddress1of2 void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2,uint8_t *priv,char *coinaddr); +/// Creates a token cryptocondition that allows to spend it by one key +/// The resulting cc will have two eval codes (EVAL_TOKENS and evalcode parameter value). +/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param pk pubkey to spend the cc +/// @returns cryptocondition object. Must be disposed with cc_free function when not used any more +CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk); + +/// Overloaded function that creates a token cryptocondition that allows to spend it by one key +/// The resulting cc will have two eval codes (EVAL_TOKENS and evalcode parameter value). +/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param evalcode2 yet another cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param pk pubkey to spend the cc +/// @returns cryptocondition object. Must be disposed with cc_free function when not used any more +CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk); + +/// Creates new 1of2 token cryptocondition that allows to spend it by either of two keys +/// Resulting vout will have three eval codes (EVAL_TOKENS, evalcode and evalcode2 parameter values). +/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param pk1 one of two pubkeys to spend the cc +/// @param pk2 second of two pubkeys to spend the cc +/// @returns cryptocondition object. Must be disposed with cc_free function when not used any more +CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2); + +/// Creates new 1of2 token cryptocondition that allows to spend it by either of two keys +/// The resulting cc will have two eval codes (EVAL_TOKENS and evalcode parameter value). +/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param evalcode2 yet another cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param pk1 one of two pubkeys to spend the cc +/// @param pk2 second of two pubkeys to spend the cc +/// @returns cryptocondition object. Must be disposed with cc_free function when not used any more +CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2); + +/// Creates a token transaction output with a cryptocondition that allows to spend it by one key. +/// The resulting vout will have two eval codes (EVAL_TOKENS and evalcode parameter value). +/// The returned output should be added to a transaction vout array. +/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param nValue value of the output in satoshi +/// @param pk pubkey to spend the cc +/// @returns vout object +/// @see CCinit +/// @see CCcontract_info +CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk); + +/// Another MakeTokensCC1vout overloaded function that creates a token transaction output with a cryptocondition with two eval codes that allows to spend it by one key. +/// Resulting vout will have three eval codes (EVAL_TOKENS, evalcode and evalcode2 parameter values). +/// The returned output should be added to a transaction vout array. +/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param evalcode2 yet another cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param nValue value of the output in satoshi +/// @param pk pubkey to spend the cc +/// @returns vout object +/// @see CCinit +/// @see CCcontract_info +CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk); + +/// MakeTokensCC1of2vout creates a token transaction output with a 1of2 cryptocondition that allows to spend it by either of two keys. +/// The resulting vout will have two eval codes (EVAL_TOKENS and evalcode parameter value). +/// The returned output should be added to a transaction vout array. +/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param nValue value of the output in satoshi +/// @param pk1 one of two pubkeys to spend the cc +/// @param pk2 second of two pubkeys to spend the cc +/// @returns vout object +/// @see CCinit +/// @see CCcontract_info +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2); + +/// Another overload of MakeTokensCC1of2vout creates a token transaction output with a 1of2 cryptocondition with two eval codes that allows to spend it by either of two keys. +/// The resulting vout will have three eval codes (EVAL_TOKENS, evalcode and evalcode2 parameter values). +/// The returned output should be added to a transaction vout array. +/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param evalcode2 yet another cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) +/// @param nValue value of the output in satoshi +/// @param pk1 one of two pubkeys to spend the cc +/// @param pk2 second of two pubkeys to spend the cc +/// @returns vout object +/// @see CCinit +/// @see CCcontract_info +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2); + +/// Gets adddress for token cryptocondition vout +/// @param cp CCcontract_info structure initialized with EVAL_TOKENS eval code +/// @param[out] destaddr retrieved address +/// @param pk public key to create the cryptocondition +bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk); + +/// Gets adddress for token 1of2 cc vout +/// @param cp CCcontract_info structure initialized with EVAL_TOKENS eval code +/// @param[out] destaddr retrieved address +/// @param pk first public key to create the cryptocondition +/// @param pk2 second public key to create the cryptocondition +bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2); + +/// CCaddrTokens1of2set sets pubkeys, private key and cc addr for spending from 1of2 token cryptocondition vout +/// @param cp contract info structure where the private key is set +/// @param pk1 one of the two public keys of the 1of2 cc +/// @param pk2 second of the two public keys of the 1of2 cc +/// @param priv private key for one of the two pubkeys +/// @param coinaddr the cc address obtained for this 1of2 token cc with GetTokensCCaddress1of2 +/// @see GetTokensCCaddress +/// @see CCcontract_info +void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, uint8_t *priv, char *coinaddr); + /// @private int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode); @@ -669,29 +752,6 @@ bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk); /// @see CCcontract_info bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubKey pk2); -/// Gets adddress for token cryptocondition vout -/// @param cp CCcontract_info structure initialized with EVAL_TOKENS eval code -/// @param[out] destaddr retrieved address -/// @param pk public key to create the cryptocondition -bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk); - -/// Gets adddress for token 1of2 cc vout -/// @param cp CCcontract_info structure initialized with EVAL_TOKENS eval code -/// @param[out] destaddr retrieved address -/// @param pk first public key to create the cryptocondition -/// @param pk2 second public key to create the cryptocondition -bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2); - -/// CCaddrTokens1of2set sets pubkeys, private key and cc addr for spending from 1of2 token cryptocondition vout -/// @param cp contract info structure where the private key is set -/// @param pk1 one of the two public keys of the 1of2 cc -/// @param pk2 second of the two public keys of the 1of2 cc -/// @param priv private key for one of the two pubkeys -/// @param coinaddr the cc address obtained for this 1of2 token cc with GetTokensCCaddress1of2 -/// @see GetTokensCCaddress -/// @see CCcontract_info -void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, uint8_t *priv, char *coinaddr); - /// @private bool ConstrainVout(CTxOut vout,int32_t CCflag,char *cmpaddr,int64_t nValue); @@ -728,19 +788,12 @@ bool Myprivkey(uint8_t myprivkey[]); /// @return duration in seconds since the block where the transaction with txid resides int64_t CCduration(int32_t &numblocks,uint256 txid); -bool ExactAmounts(Eval* eval, const CTransaction &tx, uint64_t txfee); -bool CCOpretCheck(Eval* eval, const CTransaction &tx, bool no_burn, bool no_multi, bool last_vout); - /// @private uint256 CCOraclesReverseScan(char const *logcategory,uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid); + /// @private -int64_t CCOraclesGetDepositBalance(char const *logcategory,uint256 reforacletxid,uint256 batontxid); -/// @private -int32_t CCCointxidExists(char const *logcategory,uint256 txid,uint256 cointxid); -/// @private -bool CompareHexVouts(std::string hex1, std::string hex2); -/// @private -bool CheckVinPk(const CTransaction &tx, int32_t n, std::vector &pubkeys); +int32_t CCCointxidExists(char const *logcategory,uint256 cointxid); + /// @private uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::vector &txids); @@ -808,7 +861,7 @@ void SetCCtxids(std::vector > &addressIndex /// @param evalcode evalcode of cc module for which outputs will be filtered /// @param filtertxid txid for which outputs will be filtered /// @param func funcid for which outputs will be filtered -void SetCCtxids(std::vector &txids,char *coinaddr,bool ccflag, uint8_t evalcode, int64_t amount, uint256 filtertxid, uint8_t func); +void SetCCtxids(std::vector &txids,char *coinaddr,bool ccflag, uint8_t evalcode, uint256 filtertxid, uint8_t func); /// In NSPV mode adds normal (not cc) inputs to the transaction object vin array for the specified total amount using available utxos on mypk's TX_PUBKEY address /// @param mtx mutable transaction object @@ -865,9 +918,6 @@ int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout,int32_t CCf /// @private int32_t CC_vinselect(int32_t *aboveip, int64_t *abovep, int32_t *belowip, int64_t *belowp, struct CC_utxo utxos[], int32_t numunspents, int64_t value); -/// @private -void CCAddVintxCond(struct CCcontract_info *cp, CC *cond, const uint8_t *priv = NULL); - /// @private bool NSPV_SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey,uint32_t nTime); @@ -887,121 +937,16 @@ int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey); inline std::string STR_TOLOWER(const std::string &str) { std::string out; for (std::string::const_iterator i = str.begin(); i != str.end(); i++) out += std::tolower(*i); return out; } /*! \endcond */ -/*! \cond INTERNAL */ -#define JSON_RESULT "result" -#define JSON_SUCCESS "success" -#define JSON_ERROR "error" -#define JSON_HEXTX "hex" -#define JSON_SIGDATA "SigData" - -inline bool ResultHasTx(const UniValue &result) { - return !result[JSON_HEXTX].getValStr().empty(); -} -inline std::string ResultGetTx(const UniValue &result) { - return ResultHasTx(result) ? result[JSON_HEXTX].getValStr() : std::string(); -} -inline bool ResultIsError(const UniValue &result) { - return !result.isNull() && !result[JSON_ERROR].getValStr().empty(); -} -inline std::string ResultGetError(const UniValue &result) { - if (!result.isNull()) { - if (!result[JSON_ERROR].getValStr().empty()) - return result[JSON_ERROR].getValStr(); - } - return std::string(); -} -inline UniValue MakeResultError(const std::string &err) { - UniValue result(UniValue::VOBJ); - result.pushKV(std::string(JSON_RESULT), JSON_ERROR); - result.pushKV(std::string(JSON_ERROR), err); - return result; -} -inline UniValue MakeResultSuccess(const std::string &txhex) { - UniValue result(UniValue::VOBJ); - result.pushKV(std::string(JSON_RESULT), JSON_SUCCESS); - if (!txhex.empty()) - result.pushKV(std::string(JSON_HEXTX), txhex); - return result; -} -/*! \endcond */ - - -/// @private -bool inline IS_REMOTE(const CPubKey &remotepk) { return remotepk.IsValid(); } +#define JSON_HEXTX "hex" +#define JSON_SIGDATA "SigData" /// @private add sig data for signing partially signed tx to UniValue object void AddSigData2UniValue(UniValue &result, int32_t vini, UniValue& ccjson, std::string sscriptpubkey, int64_t amount); -/// returns 0 if requirements for cc module with the evalcode is fulfilled. -/// @param evalcode eval code for cc module -/// @returns 0 if okay or -1 -int32_t ensure_CCrequirements(uint8_t evalcode); - -/// @private forward decl -struct CLockedInMemoryUtxos; - -/// @private forward decl -struct CInMemoryTxns; - -/// locking utxo functions (to prevent adding utxo to several mtx objects: -/// if in-memory utxo locking activated Addnormalinputs begins to lock in-mem utxos and will not add to mtx already locked utxos -class LockUtxoInMemory -{ -private: - static thread_local struct CLockedInMemoryUtxos utxosLocked; - static thread_local struct CInMemoryTxns txnsInMem; - - // activate locking in-memory utxos preventing adding to mtx - void activateUtxoLock(); - - // Stop locking, unlocks all locked utxos: Addnormalinputs functions will not prevent utxos from spending - void deactivateUtxoLock(); - -public: - LockUtxoInMemory(); - ~LockUtxoInMemory(); - - // returns if utxo locking is active - static bool isLockUtxoActive(); - // checks if utxo is locked (added to a mtx object) - static bool isUtxoLocked(uint256 txid, int32_t nvout); - - // lock utxo - static void LockUtxo(uint256 txid, int32_t nvout); - - static bool AddInMemoryTransaction(const CTransaction &tx); - static bool GetInMemoryTransaction(uint256 txid, CTransaction &tx); - - static void GetMyUtxosInMemory(CWallet *pWallet, bool isCC, std::vector &utxosInMem); - static void GetAddrUtxosInMemory(char *destaddr, bool isCC, std::vector &utxosInMem); -}; - -/*! \cond INTERNAL */ -void SetRemoteRPCCall(bool isRemote); -#define SET_MYPK_OR_REMOTE(mypk, remotepk) \ - if (remotepk.IsValid()) \ - SetRemoteRPCCall(true), mypk = remotepk; \ - else \ - SetRemoteRPCCall(false), mypk = pubkey2pk(Mypubkey()); - -bool IsRemoteRPCCall(); -/*! \endcond */ - -/*! \cond INTERNAL */ -UniValue CCaddress(struct CCcontract_info *cp, char *name, std::vector &pubkey); -/*! \endcond */ - -#define RETURN_IF_ERROR(CCerror) if ( CCerror != "" ) { UniValue result(UniValue::VOBJ); ERR_RESULT(CCerror); return(result); } #ifndef LOGSTREAM_DEFINED #define LOGSTREAM_DEFINED // bitcoin LogPrintStr with category "-debug" cmdarg support for C++ ostringstream: -#define CCLOG_ERROR (-1) -#define CCLOG_INFO 0 -#define CCLOG_DEBUG1 1 -#define CCLOG_DEBUG2 2 -#define CCLOG_DEBUG3 3 -#define CCLOG_MAXLEVEL 3 // log levels: #define CCLOG_ERROR (-1) //!< error level @@ -1011,32 +956,17 @@ UniValue CCaddress(struct CCcontract_info *cp, char *name, std::vector void CCLogPrintStream(const char *category, int level, const char *functionName, T print_to_stream) { std::ostringstream stream; - - stream << (category ? category : "") << " "; + print_to_stream(stream); if (functionName != NULL) stream << functionName << " "; - if (level < 0) - stream << "ERROR:" << " "; - print_to_stream(stream); CCLogPrintStr(category, level, stream.str()); } /// Macro for logging messages using bitcoin LogAcceptCategory and LogPrintStr functions. @@ -1050,12 +980,12 @@ void CCLogPrintStream(const char *category, int level, const char *functionName, /// @param logoperator to form the log message (the 'stream' name is mandatory) /// usage: LOGSTREAM("category", debug-level, stream << "some log data" << data2 << data3 << ... << std::endl); /// example: LOGSTREAM("heir", CCLOG_INFO, stream << "heir public key is " << HexStr(heirPk) << std::endl); -#define LOGSTREAM(category, level, logoperator) CCLogPrintStream( category, level, NULL, [&](std::ostringstream &stream) {logoperator;} ) +#define LOGSTREAM(category, level, logoperator) CCLogPrintStream( category, level, NULL, [=](std::ostringstream &stream) {logoperator;} ) /// LOGSTREAMFN is a version of LOGSTREAM macro which adds calling function name with the standard define \_\_func\_\_ at the beginning of the printed string. /// LOGSTREAMFN parameters are the same as in LOGSTREAM /// @see LOGSTREAM -#define LOGSTREAMFN(category, level, logoperator) CCLogPrintStream( category, level, __func__, [&](std::ostringstream &stream) {logoperator;} ) +#define LOGSTREAMFN(category, level, logoperator) CCLogPrintStream( category, level, __func__, [=](std::ostringstream &stream) {logoperator;} ) /// @private template @@ -1065,8 +995,6 @@ UniValue report_ccerror(const char *category, int level, T print_to_stream) std::ostringstream stream; print_to_stream(stream); - stream << std::endl; - err.push_back(Pair("result", "error")); err.push_back(Pair("error", stream.str())); stream << std::endl; @@ -1075,6 +1003,6 @@ UniValue report_ccerror(const char *category, int level, T print_to_stream) } /// @private -#define CCERR_RESULT(category,level,logoperator) return report_ccerror(category, level, [&](std::ostringstream &stream) {logoperator;}) +#define CCERR_RESULT(category,level,logoperator) return report_ccerror(category, level, [=](std::ostringstream &stream) {logoperator;}) #endif // #ifndef LOGSTREAM_DEFINED #endif diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index 45851cac24c..054e45f9fe4 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -14,18 +14,128 @@ ******************************************************************************/ #include "CCtokens.h" -#include "old/CCtokens_v0.h" #include "importcoin.h" /* TODO: correct this: - tokens cc tx creation and validation code +----------------------------- + The SetTokenFillamounts() and ValidateTokenRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively. + + This pair of functions are critical to make sure the trading is correct and is the trickiest part of the tokens contract. + + //vin.0: normal input + //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0] + //vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue + //vout.0: remaining amount of bid to unspendable + //vout.1: vin.1 value to signer of vin.2 + //vout.2: vin.2 tokenoshis to original pubkey + //vout.3: CC output for tokenoshis change (if any) + //vout.4: normal output for change (if any) + //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [tokenid] [remaining token required] [origpubkey] + ValidateTokenRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits); + + Yes, this is quite confusing... + + In ValidateTokenRemainder the naming convention is nValue is the coin/token with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or tokens, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits. + + We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it. + ------------------------------ */ + +// tx validation +bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) +{ + static uint256 zero; + CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2; + int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts; + int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore; + std::vector> oprets; + vscript_t /*vopretExtra,*/ tmporigpubkey, ignorepubkey; + uint8_t funcid, evalCodeInOpret; + char destaddr[64], origaddr[64], CCaddr[64]; + std::vector voutTokenPubkeys, vinTokenPubkeys; + + if (strcmp(ASSETCHAINS_SYMBOL, "ROGUE") == 0 && chainActive.Height() <= 12500) + return true; + + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + outputs = inputs = 0; + preventCCvins = preventCCvouts = -1; + + // check boundaries: + if (numvouts < 1) + return eval->Invalid("no vouts"); + + if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets)) == 0) + return eval->Invalid("TokenValidate: invalid opreturn payload"); + + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokensValidate funcId=" << (char)(funcid?funcid:' ') << " evalcode=" << std::hex << (int)cp->evalcode << std::endl); + + if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0) + return eval->Invalid("cant find token create txid"); + //else if (IsCCInput(tx.vin[0].scriptSig) != 0) + // return eval->Invalid("illegal token vin0"); // <-- this validation was removed because some token tx might not have normal vins + else if (funcid != 'c') + { + if (tokenid == zeroid) + return eval->Invalid("illegal tokenid"); + else if (!TokensExactAmounts(true, cp, inputs, outputs, eval, tx, tokenid)) { + if (!eval->Valid()) + return false; //TokenExactAmounts must call eval->Invalid()! + else + return eval->Invalid("tokens cc inputs != cc outputs"); + } + } + + // validate spending from token cc addr: allowed only for burned non-fungible tokens: + if (ExtractTokensCCVinPubkeys(tx, vinTokenPubkeys) && std::find(vinTokenPubkeys.begin(), vinTokenPubkeys.end(), GetUnspendable(cp, NULL)) != vinTokenPubkeys.end()) { + // validate spending from token unspendable cc addr: + int64_t burnedAmount = HasBurnedTokensvouts(cp, eval, tx, tokenid); + if (burnedAmount > 0) { + vscript_t vopretNonfungible; + GetNonfungibleData(tokenid, vopretNonfungible); + if( vopretNonfungible.empty() ) + return eval->Invalid("spending cc marker not supported for fungible tokens"); + } + } + + switch (funcid) + { + case 'c': // token create should not be validated as it has no CC inputs, so return 'invalid' + // token tx structure for 'c': + //vin.0: normal input + //vout.0: issuance tokenoshis to CC + //vout.1: normal output for change (if any) + //vout.n-1: opreturn EVAL_TOKENS 'c' + return eval->Invalid("incorrect token funcid"); + + case 't': // transfer + // token tx structure for 't' + //vin.0: normal input + //vin.1 .. vin.n-1: valid CC outputs + //vout.0 to n-2: tokenoshis output to CC + //vout.n-2: normal output for change (if any) + //vout.n-1: opreturn EVAL_TOKENS 't' tokenid + if (inputs == 0) + return eval->Invalid("no token inputs for transfer"); + + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "token transfer preliminarily validated inputs=" << inputs << "->outputs=" << outputs << " preventCCvins=" << preventCCvins<< " preventCCvouts=" << preventCCvouts << std::endl); + break; // breaking to other contract validation... + + default: + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "illegal tokens funcid=" << (char)(funcid?funcid:' ') << std::endl); + return eval->Invalid("unexpected token funcid"); + } + + return true; +} + // helper funcs: // extract cc token vins' pubkeys: -static bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector &vinPubkeys) { +bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector &vinPubkeys) { bool found = false; CPubKey pubkey; @@ -69,78 +179,66 @@ static bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector voutPubkeysDummy; - std::vector opretsDummy; + std::vector> opretsDummy; // this is just for log messages indentation fur debugging recursive calls: std::string indentStr = std::string().append(tokenValIndentSize, '.'); - //if (tx.vout.size() == 0) - // return (uint8_t)0; + if (tx.vout.size() == 0) + return (uint8_t)0; - if ((funcid = DecodeTokenOpRetV1(scriptPubKey, tokenidOpret, voutPubkeysDummy, opretsDummy)) == 0) + if ((funcid = DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeysDummy, opretsDummy)) == 0) { - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << indentStr << "could not parse opret for txid=" << txid.GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl); return (uint8_t)0; } - else if (IsTokenCreateFuncid(funcid)) + else if (funcid == 'c') { - if (tokenid != zeroid && tokenid == txid) { - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "this is tokenbase 'c' tx, txid=" << txid.GetHex() << " returning true" << std::endl); + if (tokenid != zeroid && tokenid == tx.GetHash()) { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); return funcid; } else { - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "not my tokenbase txid=" << txid.GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenbase txid=" << tx.GetHash().GetHex() << std::endl); } } - /* 'i' not used else if (funcid == 'i') { if (tokenid != zeroid && tokenid == tx.GetHash()) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is import 'i' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is import 'i' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); return funcid; } else { - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my import txid=" << tx.GetHash().GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my import txid=" << tx.GetHash().GetHex() << std::endl); } - }*/ - else if (IsTokenTransferFuncid(funcid)) + } + else if (funcid == 't') { //std::cerr << indentStr << "ValidateTokenOpret() tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; if (tokenid != zeroid && tokenid == tokenidOpret) { - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "this is a transfer 't' tx, txid=" << txid.GetHex() << " returning true" << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); return funcid; } else { - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "not my tokenid=" << tokenidOpret.GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenid=" << tokenidOpret.GetHex() << std::endl); } } else { - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "not supported funcid=" << (char)funcid << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << txid.GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not supported funcid=" << (char)funcid << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl); } return (uint8_t)0; } - // remove token->unspendablePk (it is only for marker usage) void FilterOutTokensUnspendablePk(const std::vector &sourcePubkeys, std::vector &destPubkeys) { struct CCcontract_info *cpTokens, tokensC; @@ -154,189 +252,44 @@ void FilterOutTokensUnspendablePk(const std::vector &sourcePubkeys, std } -// TODO: move it to CCutils.cpp -// get OP_DROP data: -bool MyGetCCopretV2(const CScript &scriptPubKey, CScript &opret) -{ - std::vector> vParams; - CScript dummy; - - if (scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) != 0) - { - if (vParams.size() >= 1) // allow more data after cc opret - { - //uint8_t version; - //uint8_t evalCode; - //uint8_t m, n; - std::vector< vscript_t > vData; - - // parse vParams[0] as script - CScript inScript(vParams[0].begin(), vParams[0].end()); - CScript::const_iterator pc = inScript.begin(); - inScript.GetPushedData(pc, vData); - - if (vData.size() > 1 && vData[0].size() == 4) // first vector is 4-byte header - { - //vscript_t vopret(vParams[0].begin() + 6, vParams[0].end()); - opret << OP_RETURN << vData[1]; // return vData[1] as cc opret - return true; - } - } - } - return false; -} - -// checks if any token vouts are sent to 'dead' pubkey -CAmount HasBurnedTokensvouts(const CTransaction& tx, uint256 reftokenid) -{ - uint8_t dummyEvalCode; - uint256 tokenIdOpret; - std::vector vDeadPubkeys, voutPubkeysDummy; - std::vector oprets; - vscript_t vopretExtra, vopretNonfungible; - - uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim - uint8_t evalCode2 = 0; // will be checked if zero or not - - // test vouts for possible token use-cases: - std::vector> testVouts; - - int32_t n = tx.vout.size(); - // just check boundaries: - if (n == 0) { - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "incorrect params: tx.vout.size() == 0, txid=" << tx.GetHash().GetHex() << std::endl); - return(0); - } - - if (DecodeTokenOpRetV1(tx.vout.back().scriptPubKey, tokenIdOpret, voutPubkeysDummy, oprets) == 0) { - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "cannot parse opret DecodeTokenOpRet returned 0, txid=" << tx.GetHash().GetHex() << std::endl); - return 0; - } - - // get assets/channels/gateways token data: - //FilterOutNonCCOprets(oprets, vopretExtra); - // NOTE: only 1 additional evalcode in token opret is currently supported - if (oprets.size() > 0) - vopretExtra = oprets[0]; - - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG2, stream << "vopretExtra=" << HexStr(vopretExtra) << std::endl); - - GetNonfungibleData(reftokenid, vopretNonfungible); +void FilterOutNonCCOprets(const std::vector> &oprets, vscript_t &vopret) { - if (vopretNonfungible.size() > 0) - evalCode = vopretNonfungible.begin()[0]; - if (vopretExtra.size() > 0) - evalCode2 = vopretExtra.begin()[0]; + vopret.clear(); - if (evalCode == EVAL_TOKENS && evalCode2 != 0) { - evalCode = evalCode2; - evalCode2 = 0; - } - - vDeadPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY))); + if (oprets.size() > 2) + LOGSTREAM("cctokens", CCLOG_INFO, stream << "FilterOutNonCCOprets() warning!! oprets.size > 2 currently not supported" << oprets.size() << std::endl); - CAmount burnedAmount = 0; - - for (int i = 0; i < tx.vout.size(); i++) - { - if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition()) - { - // make all possible token vouts for dead pk: - for (std::vector::iterator it = vDeadPubkeys.begin(); it != vDeadPubkeys.end(); it++) - { - testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[i].nValue, *it), std::string("single-eval cc1 burn pk"))); - if (evalCode != EVAL_TOKENS) - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, 0, tx.vout[i].nValue, *it), std::string("two-eval cc1 burn pk"))); - if (evalCode2 != 0) { - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[i].nValue, *it), std::string("three-eval cc1 burn pk"))); - // also check in backward evalcode order: - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[i].nValue, *it), std::string("three-eval cc1 burn pk backward-eval"))); - } - } - - // try all test vouts: - for (const auto &t : testVouts) { - if (t.first == tx.vout[i]) { - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "burned amount=" << tx.vout[i].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl); - burnedAmount += tx.vout[i].nValue; - break; // do not calc vout twice! - } - } - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG2, stream << "total burned=" << burnedAmount << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + for (auto o : oprets) { + if (o.first < OPRETID_FIRSTNONCCDATA) { // skip burn, import, etc opret data + vopret = o.second; // return first contract opret (more than 1 is not supported yet) + break; } } - - return burnedAmount; } -// validate spending markers from token global cc addr: this is allowed only for burned non-fungible tokens -// returns false if there is marker spending and it is prohibited -// returns true if no marker spending or it is allowed -static bool CheckMarkerSpending(struct CCcontract_info *cp, Eval *eval, const CTransaction &tx, uint256 tokenid) +// Checks if the vout is a really Tokens CC vout +// also checks tokenid in opret or txid if this is 'c' tx +// goDeeper is true: the func also validates amounts of the passed transaction: +// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx +// checkPubkeys is true: validates if the vout is token vout1 or token vout1of2. Should always be true! +int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true*/, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid) { - for (const auto &vin : tx.vin) - { - // validate spending from token unspendable cc addr: - const CPubKey tokenGlobalPk = GetUnspendable(cp, NULL); - if (cp->ismyvin(vin.scriptSig) && check_signing_pubkey(vin.scriptSig) == tokenGlobalPk) - { - bool allowed = false; - - if (vin.prevout.hash == tokenid) // check if this is my marker - { - // calc burned amount - CAmount burnedAmount = HasBurnedTokensvouts(tx, tokenid); - if (burnedAmount > 0) - { - vscript_t vopretNonfungible; - GetNonfungibleData(tokenid, vopretNonfungible); - if (!vopretNonfungible.empty()) - { - CTransaction tokenbaseTx; - uint256 hashBlock; - if (myGetTransaction(tokenid, tokenbaseTx, hashBlock)) - { - // get total supply - CAmount supply = 0L, output; - for (int v = 0; v < tokenbaseTx.vout.size() - 1; v++) - if ((output = IsTokensvout(false, true, cp, NULL, tokenbaseTx, v, tokenid)) > 0) - supply += output; - - if (supply == 1 && supply == burnedAmount) // burning marker is allowed only for burned NFTs (that is with supply == 1) - allowed = true; - } - } - } - } - if (!allowed) - return false; - } - } - return true; -} -// internal function to check if token vout is valid -// returns amount or -1 -// return also tokenid -static int64_t CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true*/, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 &reftokenid, std::string &errorStr) -{ // this is just for log messages indentation fur debugging recursive calls: std::string indentStr = std::string().append(tokenValIndentSize, '.'); - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl); int32_t n = tx.vout.size(); // just check boundaries: - if (n == 0 || v < 0 || v >= n) { - LOGSTREAM(cctokens_log, CCLOG_INFO, stream << indentStr << "isTokensvout() incorrect params: (n == 0 or v < 0 or v >= n)" << " v=" << v << " n=" << n << " returning error" << std::endl); - errorStr = "out of bounds"; - return(-1); + if (n == 0 || v < 0 || v >= n-1) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "isTokensvout() incorrect params: (n == 0 or v < 0 or v >= n-1)" << " v=" << v << " n=" << n << " returning 0" << std::endl); + return(0); } if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition()) { - /* old code recursively checking vintx - if (goDeeper) { + if (goDeeper) { //validate all tx int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0; @@ -349,238 +302,40 @@ static int64_t CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, a // if ccInputs != ccOutputs and it is not the tokenbase tx // this means it is possibly a fake tx (dimxy): if (reftokenid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy) - LOGSTREAM(cctokens_log, CCLOG_ERROR, stream << indentStr << "IsTokensvout() warning: for the validated tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not 'tokenbase' tx, skipping the validated tx" << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() warning: for the verified tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx" << std::endl); return 0; } } - }*/ - - // instead of recursively checking tx just check that the tx has token cc vin, that is it was validated by tokens cc module - bool hasMyccvin = false; - for (auto const vin : tx.vin) { - if (cp->ismyvin(vin.scriptSig)) { - hasMyccvin = true; - break; - } - } - - - CScript opret; - bool isLastVoutOpret; - if (MyGetCCopretV2(tx.vout[v].scriptPubKey, opret)) - { - isLastVoutOpret = false; - } - else - { - isLastVoutOpret = true; - opret = tx.vout.back().scriptPubKey; - } - - uint256 tokenIdOpret; - std::vector oprets; - std::vector voutPubkeysInOpret; - - // token opret most important checks (tokenid == reftokenid, tokenid is non-zero, tx is 'tokenbase'): - uint8_t funcId = DecodeTokenOpRetV1(opret, tokenIdOpret, voutPubkeysInOpret, oprets); - if (funcId == 0) { - // bad opreturn - // errorStr = "can't decode opreturn data"; - // return -1; - return 0; // not token vout, skip - } - - // basic checks: - if (IsTokenCreateFuncid(funcId)) { - if (hasMyccvin) { - errorStr = "tokenbase tx cannot have cc vins"; - return -1; - } - // set returned tokend to tokenbase txid: - reftokenid = tx.GetHash(); - } - else if (IsTokenTransferFuncid(funcId)) { - if (!hasMyccvin) { - errorStr = "no token cc vin in token transaction (and not tokenbase tx)"; - return -1; - } - // set returned tokenid to tokenid in opreturn: - reftokenid = tokenIdOpret; - } - else { - errorStr = "funcid not supported"; - return -1; - } - - - if (!isLastVoutOpret) // check OP_DROP vouts: - { - // get up to two eval codes from cc data: - uint8_t evalCode1 = 0, evalCode2 = 0; - if (oprets.size() >= 1) { - evalCode1 = oprets[0].size() > 0 ? oprets[0][0] : 0; - if (oprets.size() >= 2) - evalCode2 = oprets[1].size() > 0 ? oprets[1][0] : 0; - } - - // get optional nft eval code: - vscript_t vopretNonfungible; - GetNonfungibleData(reftokenid, vopretNonfungible); - if (vopretNonfungible.size() > 0) { - // shift evalcodes so the first is NFT evalcode - evalCode2 = evalCode1; - evalCode1 = vopretNonfungible[0]; - } - - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() for txid=" << tx.GetHash().GetHex() << " checking evalCode1=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " voutPubkeysInOpret.size()=" << voutPubkeysInOpret.size() << std::endl); - - if (IsTokenTransferFuncid(funcId)) - { - // check if not sent to globalpk: - for (const auto &pk : voutPubkeysInOpret) { - if (pk == GetUnspendable(cp, NULL)) { - errorStr = "cannot send tokens to global pk"; - return -1; - } - } - - // test the vout if it is a tokens vout with or withouts other cc modules eval codes: - if (voutPubkeysInOpret.size() == 1) - { - if (evalCode1 == 0 && evalCode2 == 0) { - if (IsEqualVouts(tx.vout[v], MakeTokensCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeysInOpret[0]))) - return tx.vout[v].nValue; - } - else if (evalCode1 != 0 && evalCode2 == 0) { - if (IsEqualVouts(tx.vout[v], MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, voutPubkeysInOpret[0]))) - return tx.vout[v].nValue; - } - else if (evalCode1 != 0 && evalCode2 != 0) { - if (IsEqualVouts(tx.vout[v], MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeysInOpret[0]))) - return tx.vout[v].nValue; - } - else { - errorStr = "evalCode1 is null"; - return -1; - } - } - else if (voutPubkeysInOpret.size() == 2) - { - if (evalCode1 == 0 && evalCode2 == 0) { - if (IsEqualVouts(tx.vout[v], MakeTokensCC1of2vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeysInOpret[0], voutPubkeysInOpret[1]))) - return tx.vout[v].nValue; - } - else if (evalCode1 != 0 && evalCode2 == 0) { - if (IsEqualVouts(tx.vout[v], MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeysInOpret[0], voutPubkeysInOpret[1]))) - return tx.vout[v].nValue; - } - else if (evalCode1 != 0 && evalCode2 != 0) { - if (IsEqualVouts(tx.vout[v], MakeTokensCC1of2vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeysInOpret[0], voutPubkeysInOpret[1]))) - return tx.vout[v].nValue; - } - else { - errorStr = "evalCode1 is null"; - return -1; - } - } - else - { - errorStr = "pubkeys size should be 1 or 2"; - return -1; - } - } - else - { - // funcid == 'c' - if (tx.IsCoinImport()) { - // imported coin is checked in EvalImportCoin - if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker - return tx.vout[v].nValue; - else - return 0; - } - - vscript_t vorigPubkey; - std::string dummyName, dummyDescription; - std::vector oprets; - - if (DecodeTokenCreateOpRetV1(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, oprets) == 0) { - LOGSTREAM(cctokens_log, CCLOG_INFO, stream << indentStr << "IsTokensvout() could not decode create opret" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); - return 0; - } - - CPubKey origPubkey = pubkey2pk(vorigPubkey); - vuint8_t vopretNFT; - GetOpReturnCCBlob(oprets, vopretNFT); - - // calc cc outputs for origPubkey - int64_t ccOutputs = 0; - for (const auto &vout : tx.vout) - if (vout.scriptPubKey.IsPayToCryptoCondition()) { - CTxOut testvout = vopretNFT.size() == 0 ? MakeCC1vout(EVAL_TOKENS, vout.nValue, origPubkey) : MakeTokensCC1vout(vopretNFT[0], vout.nValue, origPubkey); - if (IsEqualVouts(vout, testvout)) - ccOutputs += vout.nValue; - } - - int64_t normalInputs = TotalPubkeyNormalInputs(tx, origPubkey); // calc normal inputs really signed by originator pubkey (someone not cheating with originator pubkey) - if (normalInputs >= ccOutputs) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() assured normalInputs >= ccOutputs" << " for tokenbase=" << reftokenid.GetHex() << std::endl); - - // make test vout for origpubkey (either for fungible or NFT): - CTxOut testvout = vopretNFT.size() == 0 ? MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, origPubkey) : MakeTokensCC1vout(vopretNFT[0], tx.vout[v].nValue, origPubkey); - if (IsEqualVouts(tx.vout[v], testvout)) // check vout sent to orig pubkey - return tx.vout[v].nValue; - else - return 0; - } - else { - LOGSTREAM(cctokens_log, CCLOG_INFO, stream << indentStr << "IsTokensvout() skipping vout not fulfilled normalInputs >= ccOutput" << " for tokenbase=" << reftokenid.GetHex() << " normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << std::endl); - errorStr = "tokenbase tx issued by not pubkey in opret"; - return -1; - } - } - } - else - { - // check vout with last vout OP_RETURN - - // token opret most important checks (tokenid == reftokenid, tokenid is non-zero, tx is 'tokenbase'): - const uint8_t funcId = ValidateTokenOpret(tx.GetHash(), tx.vout.back().scriptPubKey, reftokenid); - if (funcId == 0) - { - // bad opreturn - errorStr = "can't decode opreturn data"; - return -1; - } + } - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null funcId=" << (char)(funcId ? funcId : ' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + // token opret most important checks (tokenid == reftokenid, tokenid is non-zero, tx is 'tokenbase'): + const uint8_t funcId = ValidateTokenOpret(tx, reftokenid); + //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; + if (funcId != 0) { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null funcId=" << (char)(funcId ? funcId : ' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + uint8_t dummyEvalCode; + uint256 tokenIdOpret; + std::vector voutPubkeys, voutPubkeysInOpret; vscript_t vopretExtra, vopretNonfungible; + std::vector> oprets; - // MakeTokenCCVout functions support up to two evalcodes in vouts - // We assume one of them could be a cc module working with tokens like assets, gateways or heir - // another eval code could be for a cc module responsible to non-fungible token data uint8_t evalCodeNonfungible = 0; uint8_t evalCode1 = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim - uint8_t evalCode2 = 0; // will be checked if zero or not + uint8_t evalCode2 = 0; // will be checked if zero or not // test vouts for possible token use-cases: std::vector> testVouts; - uint8_t version; - DecodeTokenOpRetV1(tx.vout.back().scriptPubKey, tokenIdOpret, voutPubkeysInOpret, oprets); - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << "IsTokensvout() oprets.size()=" << oprets.size() << std::endl); + DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeysInOpret, oprets); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() oprets.size()=" << oprets.size() << std::endl); - // get assets/channels/gateways token data in vopretExtra: - //FilterOutNonCCOprets(oprets, vopretExtra); - // NOTE: only 1 additional evalcode in token opret is currently supported: - if (oprets.size() > 0) - vopretExtra = oprets[0]; - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl); + // get assets/channels/gateways token data: + FilterOutNonCCOprets(oprets, vopretExtra); // NOTE: only 1 additional evalcode in token opret is currently supported + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl); // get non-fungible data GetNonfungibleData(reftokenid, vopretNonfungible); - std::vector voutPubkeys; FilterOutTokensUnspendablePk(voutPubkeysInOpret, voutPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there) // NOTE: evalcode order in vouts is important: @@ -596,23 +351,22 @@ static int64_t CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, a evalCode2 = 0; } - if (IsTokenTransferFuncid(funcId)) - { + if( /*checkPubkeys &&*/ funcId != 'c' ) { // for 'c' there is no pubkeys // verify that the vout is token by constructing vouts with the pubkeys in the opret: - // maybe this is dual-eval 1 pubkey or 1of2 pubkey vout? - if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) { - // check dual/three-eval 1 pubkey vout with the first pubkey + // maybe this is dual-eval 1 pubkey or 1of2 pubkey vout? + if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) { + // check dual/three-eval 1 pubkey vout with the first pubkey testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0]")) ); if (evalCode2 != 0) // also check in backward evalcode order testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0] backward-eval")) ); - if(voutPubkeys.size() == 2) { - // check dual/three eval 1of2 pubkeys vout - testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2")) ); + if(voutPubkeys.size() == 2) { + // check dual/three eval 1of2 pubkeys vout + testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2")) ); // check dual/three eval 1 pubkey vout with the second pubkey - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1]"))); + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1]"))); if (evalCode2 != 0) { // also check in backward evalcode order: // check dual/three eval 1of2 pubkeys vout @@ -620,8 +374,8 @@ static int64_t CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, a // check dual/three eval 1 pubkey vout with the second pubkey testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1] backward-eval"))); } - } - + } + // maybe this is like gatewayclaim to single-eval token? if( evalCodeNonfungible == 0 ) // do not allow to convert non-fungible to fungible token testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]), std::string("single-eval cc1 pk[0]"))); @@ -633,40 +387,37 @@ static int64_t CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, a testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval2-token cc1 pk[0]"))); // the same for pk[1]: - if (voutPubkeys.size() == 2) { + if (voutPubkeys.size() == 2) { if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]), std::string("single-eval cc1 pk[1]"))); if (evalCode1 != 0) testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval-token cc1 pk[1]"))); if (evalCode2 != 0) testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval2-token cc1 pk[1]"))); - } - } + } + } - if (voutPubkeys.size() > 0) // we could pass empty pubkey array + //special check for tx when spending from 1of2 CC address and one of pubkeys is global CC pubkey + struct CCcontract_info *cpEvalCode1,CEvalCode1; + cpEvalCode1 = CCinit(&CEvalCode1,evalCode1); + CPubKey pk=GetUnspendable(cpEvalCode1,0); + testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval1 pegscc cc1of2 pk[0] globalccpk")) ); + if (voutPubkeys.size() == 2) testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval1 pegscc cc1of2 pk[1] globalccpk")) ); + if (evalCode2!=0) { - //special check for tx when spending from 1of2 CC address and one of pubkeys is global CC pubkey - struct CCcontract_info *cpEvalCode1, CEvalCode1; - cpEvalCode1 = CCinit(&CEvalCode1, evalCode1); - CPubKey pk = GetUnspendable(cpEvalCode1, 0); - testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval1 pegscc cc1of2 pk[0] globalccpk"))); - if (voutPubkeys.size() == 2) testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval1 pegscc cc1of2 pk[1] globalccpk"))); - if (evalCode2 != 0) - { - struct CCcontract_info *cpEvalCode2, CEvalCode2; - cpEvalCode2 = CCinit(&CEvalCode2, evalCode2); - CPubKey pk = GetUnspendable(cpEvalCode2, 0); - testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval2 pegscc cc1of2 pk[0] globalccpk"))); - if (voutPubkeys.size() == 2) testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval2 pegscc cc1of2 pk[1] globalccpk"))); - } + struct CCcontract_info *cpEvalCode2,CEvalCode2; + cpEvalCode2 = CCinit(&CEvalCode2,evalCode2); + CPubKey pk=GetUnspendable(cpEvalCode2,0); + testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval2 pegscc cc1of2 pk[0] globalccpk")) ); + if (voutPubkeys.size() == 2) testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval2 pegscc cc1of2 pk[1] globalccpk")) ); } - // maybe it is single-eval or dual/three-eval token change? - std::vector vinPubkeys, vinPubkeysUnfiltered; - ExtractTokensCCVinPubkeys(tx, vinPubkeysUnfiltered); + // maybe it is single-eval or dual/three-eval token change? + std::vector vinPubkeys, vinPubkeysUnfiltered; + ExtractTokensCCVinPubkeys(tx, vinPubkeysUnfiltered); FilterOutTokensUnspendablePk(vinPubkeysUnfiltered, vinPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there) - for(std::vector::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) { + for(std::vector::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) { if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it), std::string("single-eval cc1 self vin pk"))); testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk"))); @@ -674,35 +425,33 @@ static int64_t CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, a if (evalCode2 != 0) // also check in backward evalcode order: testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk backward-eval"))); - } + } // try all test vouts: - for (const auto &t : testVouts) { + for (auto t : testVouts) { if (t.first == tx.vout[v]) { // test vout matches - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() valid amount=" << tx.vout[v].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() valid amount=" << tx.vout[v].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl); return tx.vout[v].nValue; } } - } - else - { - // funcid == 'c' - if (!tx.IsCoinImport()) - { + + } + else { // funcid == 'c' + + if (!tx.IsCoinImport()) { + vscript_t vorigPubkey; std::string dummyName, dummyDescription; - std::vector oprets; - uint8_t version; + std::vector> oprets; - if (DecodeTokenCreateOpRetV1(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, oprets) == 0) { - LOGSTREAM(cctokens_log, CCLOG_INFO, stream << indentStr << "IsTokensvout() could not decode create opret" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + if (DecodeTokenCreateOpRet(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, oprets) == 0) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() could not decode create opret" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); return 0; } CPubKey origPubkey = pubkey2pk(vorigPubkey); - vuint8_t vopretNFT; - GetOpReturnCCBlob(oprets, vopretNFT); - + + // TODO: add voutPubkeys for 'c' tx /* this would not work for imported tokens: @@ -712,35 +461,28 @@ static int64_t CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, a testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, origPubkey), std::string("single-eval cc1 orig-pk"))); // maybe this is like FillSell for non-fungible token? if (evalCode1 != 0) - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, origPubkey), std::string("dual-eval-token cc1 orig-pk"))); - */ + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, origPubkey), std::string("dual-eval-token cc1 orig-pk"))); */ - // for tokenbase tx check that normal inputs sent from origpubkey > cc outputs - // that is, tokenbase tx should be created with inputs signed by the original pubkey + // note: this would not work if there are several pubkeys in the tokencreator's wallet (AddNormalinputs does not use pubkey param): + // for tokenbase tx check that normal inputs sent from origpubkey > cc outputs int64_t ccOutputs = 0; - for (const auto &vout : tx.vout) - if (vout.scriptPubKey.IsPayToCryptoCondition()) { - CTxOut testvout = vopretNFT.size() == 0 ? MakeCC1vout(EVAL_TOKENS, vout.nValue, origPubkey) : MakeTokensCC1vout(vopretNFT[0], vout.nValue, origPubkey); - if (IsEqualVouts(vout, testvout)) - ccOutputs += vout.nValue; - } + for (auto vout : tx.vout) + if (vout.scriptPubKey.IsPayToCryptoCondition() //TODO: add voutPubkey validation + && !IsTokenMarkerVout(vout)) // should not be marker here + ccOutputs += vout.nValue; int64_t normalInputs = TotalPubkeyNormalInputs(tx, origPubkey); // check if normal inputs are really signed by originator pubkey (someone not cheating with originator pubkey) - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << " for tokenbase=" << reftokenid.GetHex() << std::endl); + LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << " for tokenbase=" << reftokenid.GetHex() << std::endl); if (normalInputs >= ccOutputs) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() assured normalInputs >= ccOutputs" << " for tokenbase=" << reftokenid.GetHex() << std::endl); - - // make test vout for origpubkey (either for fungible or NFT): - CTxOut testvout = vopretNFT.size() == 0 ? MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, origPubkey) : MakeTokensCC1vout(vopretNFT[0], tx.vout[v].nValue, origPubkey); - - if (IsEqualVouts(tx.vout[v], testvout)) // check vout sent to orig pubkey + LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() assured normalInputs >= ccOutputs" << " for tokenbase=" << reftokenid.GetHex() << std::endl); + if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker return tx.vout[v].nValue; else return 0; // vout is good, but do not take marker into account } else { - LOGSTREAM(cctokens_log, CCLOG_INFO, stream << indentStr << "IsTokensvout() skipping vout not fulfilled normalInputs >= ccOutput" << " for tokenbase=" << reftokenid.GetHex() << " normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << std::endl); + LOGSTREAM("cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() skipping vout not fulfilled normalInputs >= ccOutput" << " for tokenbase=" << reftokenid.GetHex() << " normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << std::endl); } } else { @@ -750,32 +492,13 @@ static int64_t CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, a else return 0; // vout is good, but do not take marker into account } - } - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() no valid vouts evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); - } + } + LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() no valid vouts evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + } + //std::cerr << indentStr; fprintf(stderr,"IsTokensvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str()); } - return(0); // normal vout -} - -// Checks if the vout is a really Tokens CC vout. -// For this the function takes eval codes and pubkeys from the token opret and tries to construct possible token vouts -// if one of them matches to the passed vout then the passed vout is a correct token vout -// The function also checks tokenid in the opret and checks if this tx is the tokenbase tx -// If goDeeper param is true the func also validates input and output token amounts of the passed transaction: -// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c' or 'C') tx -// checkPubkeys is true: validates if the vout is token vout1 or token vout1of2. Should always be true! -int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true*/, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid) -{ - uint256 tokenIdInOpret; - std::string errorStr; - CAmount retAmount = CheckTokensvout(goDeeper, checkPubkeys, cp, eval, tx, v, tokenIdInOpret, errorStr); - if (!errorStr.empty()) - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "error=" << errorStr << std::endl); - if (retAmount < 0) - return retAmount; - if (reftokenid == tokenIdInOpret) - return retAmount; - return 0; + //std::cerr << indentStr; fprintf(stderr,"IsTokensvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN); + return(0); } bool IsTokenMarkerVout(CTxOut vout) { @@ -785,112 +508,78 @@ bool IsTokenMarkerVout(CTxOut vout) { } // compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs) -bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, std::string &errorStr) +bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 reftokenid) { CTransaction vinTx; uint256 hashBlock; int64_t tokenoshis; - //struct CCcontract_info *cpTokens, tokensC; - //cpTokens = CCinit(&tokensC, EVAL_TOKENS); + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, EVAL_TOKENS); int32_t numvins = tx.vin.size(); int32_t numvouts = tx.vout.size(); - - std::map mapinputs, mapoutputs; + inputs = outputs = 0; // this is just for log messages indentation for debugging recursive calls: std::string indentStr = std::string().append(tokenValIndentSize, '.'); - //LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "TokensExactAmounts() entered for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokensExactAmounts() entered for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); - // pick token vouts in vin transactions and calculate input total for (int32_t i = 0; iismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/) + if ((*cpTokens->ismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/) { //std::cerr << indentStr << "TokensExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl; // we are not inside the validation code -- dimxy if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock))) { - LOGSTREAM(cctokens_log, CCLOG_ERROR, stream << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl); - return (!eval) ? false : eval->Invalid("could not load vin tx " + std::to_string(i)); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl); + return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt"); } - else - { - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() checking vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl); + else { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() checking vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl); - uint256 reftokenid; // validate vouts of vintx tokenValIndentSize++; - tokenoshis = CheckTokensvout(goDeeper, true, cp, eval, vinTx, tx.vin[i].prevout.n, reftokenid, errorStr); + tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, reftokenid); tokenValIndentSize--; - if (tokenoshis < 0) - return false; - - // skip marker spending - // later it will be checked if marker spending is allowed - if (IsTokenMarkerVout(vinTx.vout[tx.vin[i].prevout.n])) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() skipping marker vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl); - continue; - } - if (tokenoshis != 0) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding vintx.vout for tx.vin[" << i << "] tokenoshis=" << tokenoshis << std::endl); - mapinputs[reftokenid] += tokenoshis; + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding vintx.vout for tx.vin[" << i << "] tokenoshis=" << tokenoshis << std::endl); + inputs += tokenoshis; } } } } - // pick token vouts in the current transaction and calculate output total - for (int32_t i = 0; i < numvouts; i ++) + for (int32_t i = 0; i < numvouts-1; i ++) // 'numvouts-1' <-- do not check opret { - uint256 reftokenid; - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() recursively checking tx.vout[" << i << "] nValue=" << tx.vout[i].nValue << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() recursively checking tx.vout[" << i << "] nValue=" << tx.vout[i].nValue << std::endl); // Note: we pass in here IsTokenvout(false,...) because we don't need to call TokenExactAmounts() recursively from IsTokensvout here // indeed, if we pass 'true' we'll be checking this tx vout again tokenValIndentSize++; - tokenoshis = CheckTokensvout(false, true, cp, eval, tx, i, reftokenid, errorStr); + tokenoshis = IsTokensvout(false /*<--do not recursion here*/, true /*<--exclude non-tokens vouts*/, cpTokens, eval, tx, i, reftokenid); tokenValIndentSize--; - if (tokenoshis < 0) - return false; - - if (IsTokenMarkerVout(tx.vout[i])) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() skipping marker tx.vout[" << i << "] nValue=" << tx.vout[i].nValue << std::endl); - continue; - } if (tokenoshis != 0) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding tx.vout[" << i << "] tokenoshis=" << tokenoshis << std::endl); - mapoutputs[reftokenid] += tokenoshis; + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding tx.vout[" << i << "] tokenoshis=" << tokenoshis << std::endl); + outputs += tokenoshis; } } //std::cerr << indentStr << "TokensExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; - if (mapinputs.size() > 0 && mapinputs.size() == mapoutputs.size()) - { - for(auto const &m : mapinputs) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() inputs[" << m.first.GetHex() << "]=" << m.second << " outputs=" << mapoutputs[m.first] << std::endl); - if (m.second != mapoutputs[m.first]) { - errorStr = "inputs not equal outputs for tokenid=" + m.first.GetHex(); - return false; - } - - // check marker spending: - if (!CheckMarkerSpending(cp, eval, tx, m.first)) { - errorStr = "spending marker is not allowed for tokenid=" + m.first.GetHex(); - return false; - } - } - return true; + if (inputs != outputs) { + if (tx.GetHash() != reftokenid) + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokenExactAmounts() found unequal token cc inputs=" << inputs << " vs cc outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << " and this is not the create tx" << std::endl); + //fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); + return false; // do not call eval->Invalid() here! } - errorStr = "empty token vins=" + std::to_string(mapinputs.size()) + " or vouts=" + std::to_string(mapoutputs.size()); - return false; + else + return true; } @@ -901,7 +590,7 @@ void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible) uint256 hashBlock; if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) { - LOGSTREAM(cctokens_log, CCLOG_INFO, stream << "GetNonfungibleData() could not load token creation tx=" << tokenid.GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "GetNonfungibleData() cound not load token creation tx=" << tokenid.GetHex() << std::endl); return; } @@ -910,137 +599,40 @@ void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible) if (tokenbasetx.vout.size() > 0) { std::vector origpubkey; std::string name, description; - std::vector oprets; - uint8_t funcid; - - if (IsTokenCreateFuncid(DecodeTokenCreateOpRetV1(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets))) { - if (oprets.size() > 0) - vopretNonfungible = oprets[0]; - } - } -} - -CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey) { - - uint8_t funcId; - uint256 tokenid; - std::vector voutTokenPubkeys; - std::vector oprets; - - if ((funcId = DecodeTokenOpRetV1(scriptPubKey, tokenid, voutTokenPubkeys, oprets)) != 0) { - CTransaction tokenbasetx; - uint256 hashBlock; + std::vector> oprets; - if (myGetTransaction(tokenid, tokenbasetx, hashBlock) && tokenbasetx.vout.size() > 0) { - vscript_t vorigpubkey; - std::string name, desc; - std::vector oprets; - if (DecodeTokenCreateOpRetV1(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, desc, oprets) != 0) - return pubkey2pk(vorigpubkey); + if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets) == 'c') { + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); } } - return CPubKey(); //return invalid pubkey } -// tx validation -// NOTE: opreturn decode v1 functions (DecodeTokenCreateOpRetV1 DecodeTokenOpRetV1) understands both old and new opreturn versions -bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) -{ - if (!TokensIsVer1Active(eval)) - return tokensv0::TokensValidate(cp, eval, tx, nIn); - - if (strcmp(ASSETCHAINS_SYMBOL, "ROGUE") == 0 && chainActive.Height() <= 12500) - return true; - - // check boundaries: - if (tx.vout.size() < 1) - return eval->Invalid("no vouts"); - std::string errorStr; - if (!TokensExactAmounts(true, cp, eval, tx, errorStr)) - { - LOGSTREAMFN(cctokens_log, CCLOG_ERROR, stream << "validation error: " << errorStr << " tx=" << HexStr(E_MARSHAL(ss << tx)) << std::endl); - if (eval->state.IsInvalid()) - return false; //TokenExactAmounts has already called eval->Invalid() - else - return eval->Invalid(errorStr); - } - return true; +// overload, adds inputs from token cc addr +int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs) { + vscript_t vopretNonfungibleDummy; + return AddTokenCCInputs(cp, mtx, pk, tokenid, total, maxinputs, vopretNonfungibleDummy); } -// overload for fungible tokens, adds token inputs from pubkey -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const CPubKey &pk, uint256 tokenid, int64_t total, int32_t maxinputs, vscript_t &vopretNonfungible) -{ - char tokenaddr[KOMODO_ADDRESS_BUFSIZE]; - - GetNonfungibleData(tokenid, vopretNonfungible); - if (vopretNonfungible.size() > 0) - cp->evalcodeNFT = vopretNonfungible.begin()[0]; // set evalcode of NFT - - GetTokensCCaddress(cp, tokenaddr, pk); // set tokenaddr, additionalTokensEvalcode2 is used if set - return AddTokenCCInputs(cp, mtx, tokenaddr, tokenid, total, maxinputs); -} - -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const CPubKey &pk, uint256 tokenid, int64_t total, int32_t maxinputs) -{ - char tokenaddr[KOMODO_ADDRESS_BUFSIZE]; - - // check if this is a NFT - vscript_t vopretNonfungible; - GetNonfungibleData(tokenid, vopretNonfungible); - if (vopretNonfungible.size() > 0) - cp->evalcodeNFT = vopretNonfungible.begin()[0]; // set evalcode of NFT - - GetTokensCCaddress(cp, tokenaddr, pk); // GetTokensCCaddress will use 'additionalTokensEvalcode2' - return AddTokenCCInputs(cp, mtx, tokenaddr, tokenid, total, maxinputs); -} - -/* -// overload for non-fungible tokens, adds token inputs from pubkey -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const CPubKey &pk, uint256 tokenid, int64_t total, int32_t maxinputs, bool useMempool) -{ - char tokenaddr[64]; - - if (cp->additionalTokensEvalcode2 == 0) // not set yet - { - // check if this is a NFT - vscript_t vopretNonfungible; - GetNonfungibleData(tokenid, vopretNonfungible); - if (vopretNonfungible.size() > 0) - cp->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; // set evalcode of NFT - } - GetTokensCCaddress(cp, tokenaddr, pk); // GetTokensCCaddress will use 'additionalTokensEvalcode2' - return AddTokenCCInputs(cp, mtx, tokenaddr, tokenid, total, maxinputs, useMempool); -} -*/ - -// overload, adds inputs from token cc addr and returns non-fungible opret payload if present +// adds inputs from token cc addr and returns non-fungible opret payload if present // also sets evalcode in cp, if needed -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const char *tokenaddr, uint256 tokenid, int64_t total, int32_t maxinputs, bool useMempool) +int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs, vscript_t &vopretNonfungible) { + char tokenaddr[64], destaddr[64]; int64_t threshold, nValue, price, totalinputs = 0; int32_t n = 0; std::vector > unspentOutputs; - if (cp->evalcode != EVAL_TOKENS) - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "warning: EVAL_TOKENS *cp is needed but used evalcode=" << (int)cp->evalcode << std::endl); - - if (cp->evalcodeNFT == 0) // if not set yet (in TransferToken or this func overload) - { - // check if this is a NFT - vscript_t vopretNonfungible; - GetNonfungibleData(tokenid, vopretNonfungible); //load NFT data - if (vopretNonfungible.size() > 0) - cp->evalcodeNFT = vopretNonfungible.begin()[0]; // set evalcode of NFT, for signing - } + GetNonfungibleData(tokenid, vopretNonfungible); + if (vopretNonfungible.size() > 0) + cp->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + + GetTokensCCaddress(cp, tokenaddr, pk); + SetCCunspents(unspentOutputs, tokenaddr,true); - //if (!useMempool) // reserved for mempool use - SetCCunspents(unspentOutputs, (char*)tokenaddr, true); - //else - // SetCCunspentsWithMempool(unspentOutputs, (char*)tokenaddr, true); // add tokens in mempool too if (unspentOutputs.empty()) { - LOGSTREAM(cctokens_log, CCLOG_INFO, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->evalcodeNFT << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->additionalTokensEvalcode2 << std::endl); } threshold = total / (maxinputs != 0 ? maxinputs : CC_MAXVINS); @@ -1052,10 +644,8 @@ int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c uint256 vintxid = it->first.txhash; int32_t vout = (int32_t)it->first.index; - //if (it->second.satoshis < threshold) // this should work also for non-fungible tokens (there should be only 1 satoshi for non-fungible token issue) - // continue; - if (it->second.satoshis == 0) - continue; + if (it->second.satoshis < threshold) // this should work also for non-fungible tokens (there should be only 1 satoshi for non-fungible token issue) + continue; int32_t ivin; for (ivin = 0; ivin < mtx.vin.size(); ivin ++) @@ -1066,30 +656,28 @@ int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c if (myGetTransaction(vintxid, vintx, hashBlock) != 0) { - char destaddr[64]; Getscriptaddress(destaddr, vintx.vout[vout].scriptPubKey); - if (strcmp(destaddr, tokenaddr) != 0 /*&& + if (strcmp(destaddr, tokenaddr) != 0 && strcmp(destaddr, cp->unspendableCCaddr) != 0 && // TODO: check why this. Should not we add token inputs from unspendable cc addr if mypubkey is used? - strcmp(destaddr, cp->unspendableaddr2) != 0*/) // or the logic is to allow to spend all available tokens (what about unspendableaddr3)? + strcmp(destaddr, cp->unspendableaddr2) != 0) // or the logic is to allow to spend all available tokens (what about unspendableaddr3)? continue; - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << "AddTokenCCInputs() check vintx vout destaddress=" << destaddr << " amount=" << vintx.vout[vout].nValue << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() check vintx vout destaddress=" << destaddr << " amount=" << vintx.vout[vout].nValue << std::endl); if ((nValue = IsTokensvout(true, true/*<--add only valid token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,vintxid, vout) == 0) { - /* no need in this check: we already have vopretNonfungible from tokenid opret - //for non-fungible tokens check payload: + //for non-fungible tokens check payload: if (!vopretNonfungible.empty()) { vscript_t vopret; // check if it is non-fungible token: GetNonfungibleData(tokenid, vopret); if (vopret != vopretNonfungible) { - LOGSTREAM(cctokens_log, CCLOG_INFO, stream << "AddTokenCCInputs() found incorrect non-fungible opret payload for vintxid=" << vintxid.GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() found incorrect non-fungible opret payload for vintxid=" << vintxid.GetHex() << std::endl); continue; } // non-fungible evalCode2 cc contract should also check if there exists only one non-fungible vout with amount = 1 - } */ + } if (total != 0 && maxinputs != 0) // if it is not just to calc amount... @@ -1097,7 +685,7 @@ int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c nValue = it->second.satoshis; totalinputs += nValue; - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << "AddTokenCCInputs() adding input nValue=" << nValue << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() adding input nValue=" << nValue << std::endl); n++; if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) @@ -1110,66 +698,140 @@ int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c return(totalinputs); } +// checks if any token vouts are sent to 'dead' pubkey +int64_t HasBurnedTokensvouts(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, uint256 reftokenid) +{ + uint8_t dummyEvalCode; + uint256 tokenIdOpret; + std::vector voutPubkeys, voutPubkeysDummy; + std::vector> oprets; + vscript_t vopretExtra, vopretNonfungible; + + uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim + uint8_t evalCode2 = 0; // will be checked if zero or not + // test vouts for possible token use-cases: + std::vector> testVouts; -std::string CreateTokenLocal(int64_t txfee, int64_t tokensupply, std::string name, std::string description, vscript_t nonfungibleData) -{ - CPubKey nullpk = CPubKey(); - UniValue sigData = CreateTokenExt(nullpk, txfee, tokensupply, name, description, nonfungibleData, 0, false); - return sigData[JSON_HEXTX].getValStr(); + int32_t n = tx.vout.size(); + // just check boundaries: + if (n == 0) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "HasBurnedTokensvouts() incorrect params: tx.vout.size() == 0, txid=" << tx.GetHash().GetHex() << std::endl); + return(0); + } + + + if (DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeysDummy, oprets) == 0) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "HasBurnedTokensvouts() cannot parse opret DecodeTokenOpRet returned 0, txid=" << tx.GetHash().GetHex() << std::endl); + return 0; + } + + // get assets/channels/gateways token data: + FilterOutNonCCOprets(oprets, vopretExtra); // NOTE: only 1 additional evalcode in token opret is currently supported + + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "HasBurnedTokensvouts() vopretExtra=" << HexStr(vopretExtra) << std::endl); + + GetNonfungibleData(reftokenid, vopretNonfungible); + + if (vopretNonfungible.size() > 0) + evalCode = vopretNonfungible.begin()[0]; + if (vopretExtra.size() > 0) + evalCode2 = vopretExtra.begin()[0]; + + if (evalCode == EVAL_TOKENS && evalCode2 != 0) { + evalCode = evalCode2; + evalCode2 = 0; + } + + voutPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY))); + + int64_t burnedAmount = 0; + + for (int i = 0; i < tx.vout.size(); i++) { + + if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition()) + { + // make all possible token vouts for dead pk: + for (std::vector::iterator it = voutPubkeys.begin(); it != voutPubkeys.end(); it++) { + testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[i].nValue, *it), std::string("single-eval cc1 burn pk"))); + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[i].nValue, *it), std::string("three-eval cc1 burn pk"))); + + if (evalCode2 != 0) + // also check in backward evalcode order: + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[i].nValue, *it), std::string("three-eval cc1 burn pk backward-eval"))); + } + + // try all test vouts: + for (auto t : testVouts) { + if (t.first == tx.vout[i]) { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "HasBurnedTokensvouts() burned amount=" << tx.vout[i].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl); + burnedAmount += tx.vout[i].nValue; + } + } + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "HasBurnedTokensvouts() total burned=" << burnedAmount << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + } + } + + return burnedAmount; +} + +CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey) { + + uint8_t funcId, evalCode; + uint256 tokenid; + std::vector voutTokenPubkeys; + std::vector> oprets; + + if ((funcId = DecodeTokenOpRet(scriptPubKey, evalCode, tokenid, voutTokenPubkeys, oprets)) != 0) { + CTransaction tokenbasetx; + uint256 hashBlock; + + if (myGetTransaction(tokenid, tokenbasetx, hashBlock) && tokenbasetx.vout.size() > 0) { + vscript_t vorigpubkey; + std::string name, desc; + if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, desc) != 0) + return pubkey2pk(vorigpubkey); + } + } + return CPubKey(); //return invalid pubkey } // returns token creation signed raw tx -// params: txfee amount, token amount, token name and description, optional NFT data, -UniValue CreateTokenExt(const CPubKey &remotepk, int64_t txfee, int64_t tokensupply, std::string name, std::string description, vscript_t nonfungibleData, uint8_t additionalMarkerEvalCode, bool addTxInMemory) +std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, std::string description, vscript_t nonfungibleData) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - struct CCcontract_info *cp, C; - UniValue sigData; - + CPubKey mypk; struct CCcontract_info *cp, C; if (tokensupply < 0) { CCerror = "negative tokensupply"; - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << CCerror << "=" << tokensupply << std::endl); - return NullUniValue; + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() =" << CCerror << "=" << tokensupply << std::endl); + return std::string(""); } if (!nonfungibleData.empty() && tokensupply != 1) { CCerror = "for non-fungible tokens tokensupply should be equal to 1"; - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << CCerror << std::endl); - return NullUniValue; + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() " << CCerror << std::endl); + return std::string(""); } + cp = CCinit(&C, EVAL_TOKENS); if (name.size() > 32 || description.size() > 4096) // this is also checked on rpc level { - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "name len=" << name.size() << " or description len=" << description.size() << " is too big" << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "name len=" << name.size() << " or description len=" << description.size() << " is too big" << std::endl); CCerror = "name should be <= 32, description should be <= 4096"; - return NullUniValue; + return(""); } if (txfee == 0) txfee = 10000; - - int32_t txfeeCount = 2; - if (additionalMarkerEvalCode > 0) - txfeeCount++; - - bool isRemote = remotepk.IsValid(); - CPubKey mypk = isRemote ? remotepk : pubkey2pk(Mypubkey()); - if (!mypk.IsFullyValid()) { - CCerror = "mypk is not set or invalid"; - return NullUniValue; - } - - CAmount totalInputs; - // always add inputs only from the mypk passed in the param to prove the token creator has the token originator pubkey - // This what the AddNormalinputsRemote does (and it is not necessary that this is done only for nspv calls): - if ((totalInputs = AddNormalinputsRemote(mtx, mypk, tokensupply + txfeeCount * txfee, 0x10000)) > 0) + mypk = pubkey2pk(Mypubkey()); + + if (AddNormalinputs2(mtx, tokensupply + 2 * txfee, 64) > 0) // add normal inputs only from mypk { int64_t mypkInputs = TotalPubkeyNormalInputs(mtx, mypk); - if (mypkInputs < tokensupply) { // check that the token amount is really issued with mypk (because in the wallet there may be some other privkeys) - CCerror = "some inputs signed not with mypubkey (-pubkey=pk)"; - return NullUniValue; + if (mypkInputs < tokensupply) { // check that tokens amount are really issued with mypk (because in the wallet there maybe other privkeys) + CCerror = "some inputs signed not with -pubkey=pk"; + return std::string(""); } - + uint8_t destEvalCode = EVAL_TOKENS; if( nonfungibleData.size() > 0 ) destEvalCode = nonfungibleData.begin()[0]; @@ -1179,387 +841,96 @@ UniValue CreateTokenExt(const CPubKey &remotepk, int64_t txfee, int64_t tokensup mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, tokensupply, mypk)); //mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); // old marker (non-burnable because spending could not be validated) //mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // ...moved to vout=0 for matching with rogue-game token - if (additionalMarkerEvalCode > 0) - { - // add additional marker for NFT cc evalcode: - struct CCcontract_info *cp2, C2; - cp2 = CCinit(&C2, additionalMarkerEvalCode); - mtx.vout.push_back(MakeCC1vout(additionalMarkerEvalCode, txfee, GetUnspendable(cp2, NULL))); - } - - //std::cerr << "mtx.before=" << HexStr(E_MARSHAL(ss << mtx)) << std::endl; - //std::cerr << "mtx.hash before=" << mtx.GetHash().GetHex() << std::endl; - - sigData = FinalizeCCTxExt(isRemote, 0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRetV1(vscript_t(mypk.begin(), mypk.end()), name, description, { nonfungibleData })); - //std::cerr << "mtx.after=" << HexStr(E_MARSHAL(ss << mtx)) << std::endl; - //std::cerr << "mtx.hash after=" << mtx.GetHash().GetHex() << std::endl; - - if (!ResultHasTx(sigData)) { - CCerror = "couldnt finalize token tx"; - return NullUniValue; - } - if (addTxInMemory) - { - // add tx to in-mem array to use in subsequent AddNormalinputs() - // LockUtxoInMemory::AddInMemoryTransaction(mtx); - } - return sigData; + return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description, nonfungibleData))); } CCerror = "cant find normal inputs"; - return NullUniValue; -} - -// transfer tokens from mypk to another pubkey -// param additionalEvalCode2 allows transfer of dual-eval non-fungible tokens -std::string TokenTransfer(int64_t txfee, uint256 tokenid, CPubKey destpubkey, int64_t total) -{ - char tokenaddr[64]; - CPubKey mypk = pubkey2pk(Mypubkey()); - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_TOKENS); - - vscript_t vopretNonfungible; - GetNonfungibleData(tokenid, vopretNonfungible); - if (vopretNonfungible.size() > 0) - cp->evalcodeNFT = vopretNonfungible.begin()[0]; // set evalcode of NFT - GetTokensCCaddress(cp, tokenaddr, mypk); - - UniValue sigData = TokenTransferExt(CPubKey(), txfee, tokenid, tokenaddr, std::vector>(), std::vector {destpubkey}, total); - return ResultGetTx(sigData); -} - - -UniValue TokenBeginTransferTx(CMutableTransaction &mtx, struct CCcontract_info *cp, const CPubKey &remotepk, CAmount txfee) -{ - bool isRemote = IS_REMOTE(remotepk); - CPubKey mypk = isRemote ? remotepk : pubkey2pk(Mypubkey()); - if (!mypk.IsFullyValid()) { - return MakeResultError("my pubkey not set"); - } - - if (!TokensIsVer1Active(NULL)) - return MakeResultError("tokens version 1 not active yet"); - - if (txfee == 0) - txfee = 10000; - - mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - int64_t normalInputs = AddNormalinputs(mtx, mypk, txfee, 3, isRemote); - if (normalInputs < 0) - { - return MakeResultError("cannot find normal inputs"); - } - return NullUniValue; -} - -UniValue TokenAddTransferVout(CMutableTransaction &mtx, struct CCcontract_info *cp, const CPubKey &remotepk, uint256 tokenid, const char *tokenaddr, std::vector destpubkeys, const std::pair &probecond, CAmount amount, bool useMempool) -{ - if (!TokensIsVer1Active(NULL)) - return MakeResultError("tokens version 1 not active yet"); - - if (amount < 0) { - CCerror = strprintf("negative amount"); - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << CCerror << "=" << amount << std::endl); - MakeResultError("negative amount"); - } - - bool isRemote = IS_REMOTE(remotepk); - CPubKey mypk = isRemote ? remotepk : pubkey2pk(Mypubkey()); - if (!mypk.IsFullyValid()) { - CCerror = "mypk is not set or invalid"; - return MakeResultError("my pubkey not set"); - } - - CAmount inputs; - if ((inputs = AddTokenCCInputs(cp, mtx, tokenaddr, tokenid, amount, CC_MAXVINS, useMempool)) > 0) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx! - { - if (inputs < amount) { - CCerror = strprintf("insufficient token inputs"); - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << CCerror << std::endl); - return MakeResultError("insufficient token inputs"); - } - - uint8_t destEvalCode = EVAL_TOKENS; - if (cp->evalcodeNFT != 0) // if set in AddTokenCCInputs - { - destEvalCode = cp->evalcodeNFT; - } - - if (probecond.first != nullptr) - { - // add probe cc and kogs priv to spend from kogs global pk - CCAddVintxCond(cp, probecond.first, probecond.second); - } - - CScript opret = EncodeTokenOpRetV1(tokenid, destpubkeys, {}); - vscript_t vopret; - GetOpReturnData(opret, vopret); - std::vector vData { vopret }; - if (destpubkeys.size() == 1) - mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, amount, destpubkeys[0], &vData)); // if destEvalCode == EVAL_TOKENS then it is actually equal to MakeCC1vout(EVAL_TOKENS,...) - else if (destpubkeys.size() == 2) - mtx.vout.push_back(MakeTokensCC1of2vout(destEvalCode, amount, destpubkeys[0], destpubkeys[1], &vData)); - else - { - CCerror = "zero or unsupported destination pk count"; - return MakeResultError("zero or unsupported destination pubkey count"); - } - - CAmount CCchange = 0L; - if (inputs > amount) - CCchange = (inputs - amount); - if (CCchange != 0) { - CScript opret = EncodeTokenOpRetV1(tokenid, {mypk}, {}); - vscript_t vopret; - GetOpReturnData(opret, vopret); - std::vector vData { vopret }; - mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, CCchange, mypk, &vData)); - } - - // add probe pubkeys to detect token vouts in tx - //std::vector voutTokenPubkeys; - //for(const auto &pk : destpubkeys) - // voutTokenPubkeys.push_back(pk); // dest pubkey(s) added to opret for validating the vout as token vout (in IsTokensvout() func) - return MakeResultSuccess(""); - } - return MakeResultError("could not find token inputs"); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() " << CCerror << std::endl); + return std::string(""); } -UniValue TokenFinalizeTransferTx(CMutableTransaction &mtx, struct CCcontract_info *cp, const CPubKey &remotepk, CAmount txfee, const CScript &opret) -{ - if (!TokensIsVer1Active(NULL)) - return MakeResultError("tokens version 1 not active yet"); - - uint64_t mask = ~((1LL << mtx.vin.size()) - 1); // seems, mask is not used anymore - bool isRemote = IS_REMOTE(remotepk); - CPubKey mypk = isRemote ? remotepk : pubkey2pk(Mypubkey()); - if (!mypk.IsFullyValid()) { - CCerror = "mypk is not set or invalid"; - return MakeResultError("my pubkey not set"); - } - - //if (evalcode2) - // cp->additionalTokensEvalcode2 = evalcode2; - - //for (const auto &p : probeconds) - // CCAddVintxCond(cp, p.first, p.second); - - // TODO maybe add also opret blobs form vintx - // as now this TokenTransfer() allows to transfer only tokens (including NFTs) that are unbound to other cc - UniValue sigData = FinalizeCCTxExt(isRemote, mask, cp, mtx, mypk, txfee, opret); - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "mtx=" << HexStr(E_MARSHAL(ss << mtx)) << std::endl); - if (ResultHasTx(sigData)) { - // LockUtxoInMemory::AddInMemoryTransaction(mtx); // to be able to spend mtx change - return sigData; - } - else - { - CCerror = "could not finalize tx"; - return MakeResultError("cannot finalize tx");; - } -} - - -// token transfer extended version -// params: -// txfee - transaction fee, assumed 10000 if 0 -// tokenid - token creation tx id -// tokenaddr - address where unspent token inputs to search -// probeconds - vector of pair of vintx cond and privkey (if null then global priv key will be used) to pick vintx token vouts to sign mtx vins -// destpubkeys - if size=1 then it is the dest pubkey, if size=2 then the dest address is 1of2 addr -// total - token amount to transfer -// returns: signed transfer tx in hex -UniValue TokenTransferExt(const CPubKey &remotepk, int64_t txfee, uint256 tokenid, const char *tokenaddr, std::vector> probeconds, std::vector destpubkeys, int64_t total, bool useMempool) +// transfer tokens to another pubkey +// param additionalEvalCode allows transfer of dual-eval non-fungible tokens +std::string TokenTransfer(int64_t txfee, uint256 tokenid, vscript_t destpubkey, int64_t total) { - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - uint64_t mask; int64_t CCchange = 0, inputs = 0; - struct CCcontract_info *cp, C; - + CPubKey mypk; uint64_t mask; int64_t CCchange = 0, inputs = 0; struct CCcontract_info *cp, C; + vscript_t vopretNonfungible, vopretEmpty; + if (total < 0) { CCerror = strprintf("negative total"); - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << CCerror << "=" << total << std::endl); - return NullUniValue; + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << CCerror << "=" << total << std::endl); + return(""); } cp = CCinit(&C, EVAL_TOKENS); if (txfee == 0) txfee = 10000; - - bool isRemote = IS_REMOTE(remotepk); - CPubKey mypk = isRemote ? remotepk : pubkey2pk(Mypubkey()); - if (!mypk.IsFullyValid()) { - CCerror = "mypk is not set or invalid"; - return NullUniValue; - } - - int64_t normalInputs = AddNormalinputs(mtx, mypk, txfee, 0x10000, isRemote); - if (normalInputs > 0) + mypk = pubkey2pk(Mypubkey()); + /*if ( cp->tokens1of2addr[0] == 0 ) + { + GetTokensCCaddress(cp, cp->tokens1of2addr, mypk); + fprintf(stderr,"set tokens1of2addr <- %s\n",cp->tokens1of2addr); + }*/ + if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) { mask = ~((1LL << mtx.vin.size()) - 1); // seems, mask is not used anymore - if ((inputs = AddTokenCCInputs(cp, mtx, tokenaddr, tokenid, total, CC_MAXVINS, useMempool)) > 0) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx! + if ((inputs = AddTokenCCInputs(cp, mtx, mypk, tokenid, total, 60, vopretNonfungible)) > 0) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx! { if (inputs < total) { //added dimxy CCerror = strprintf("insufficient token inputs"); - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << CCerror << std::endl); - return NullUniValue; + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << std::endl); + return std::string(""); } uint8_t destEvalCode = EVAL_TOKENS; - if (cp->evalcodeNFT != 0) // if set in AddTokenCCInputs - { - destEvalCode = cp->evalcodeNFT; - } + if (vopretNonfungible.size() > 0) + destEvalCode = vopretNonfungible.begin()[0]; if (inputs > total) CCchange = (inputs - total); - - if (destpubkeys.size() == 1) - mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, total, destpubkeys[0])); // if destEvalCode == EVAL_TOKENS then it is actually equal to MakeCC1vout(EVAL_TOKENS,...) - else if (destpubkeys.size() == 2) - mtx.vout.push_back(MakeTokensCC1of2vout(destEvalCode, total, destpubkeys[0], destpubkeys[1])); - else - { - CCerror = "zero or unsupported destination pk count"; - return NullUniValue; - } - + mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, total, pubkey2pk(destpubkey))); // if destEvalCode == EVAL_TOKENS then it is actually MakeCC1vout(EVAL_TOKENS,...) if (CCchange != 0) mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, CCchange, mypk)); - // add probe pubkeys to detect token vouts in tx std::vector voutTokenPubkeys; - for(auto pk : destpubkeys) - voutTokenPubkeys.push_back(pk); // dest pubkey(s) added to opret for validating the vout as token vout (in IsTokensvout() func) - - // add optional probe conds to non-usual sign vins - for (auto p : probeconds) - CCAddVintxCond(cp, p.first, p.second); - - // TODO maybe add also opret blobs form vintx - // as now this TokenTransfer() allows to transfer only tokens (including NFTs) that are unbound to other cc - UniValue sigData = FinalizeCCTxExt(isRemote, mask, cp, mtx, mypk, txfee, EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, {} )); - if (!ResultHasTx(sigData)) - CCerror = "could not finalize tx"; - //else reserved for use in memory mtx: - // LockUtxoInMemory::AddInMemoryTransaction(mtx); // to be able to spend mtx change - return sigData; - + voutTokenPubkeys.push_back(pubkey2pk(destpubkey)); // dest pubkey for validating vout + + return FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, std::make_pair((uint8_t)0, vopretEmpty))); } else { CCerror = strprintf("no token inputs"); - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << CCerror << " for amount=" << total << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << " for amount=" << total << std::endl); } //} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size()); } else { CCerror = strprintf("insufficient normal inputs for tx fee"); - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << CCerror << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << std::endl); } - return NullUniValue; + return(""); } -// transfer token to scriptPubKey -/* -UniValue TokenTransferSpk(const CPubKey &remotepk, int64_t txfee, uint256 tokenid, const char *tokenaddr, std::vector> probeconds, const CScript &spk, int64_t total, const std::vector &voutPubkeys, bool useMempool) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - int64_t CCchange = 0, inputs = 0; - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_TOKENS); - - if (total < 0) { - CCerror = strprintf("negative total"); - return NullUniValue; - } - if (txfee == 0) - txfee = 10000; - - bool isRemote = IS_REMOTE(remotepk); - CPubKey mypk = isRemote ? remotepk : pubkey2pk(Mypubkey()); - if (!mypk.IsFullyValid()) - { - CCerror = "mypk is not set or invalid"; - return NullUniValue; - } - - if (AddNormalinputs(mtx, mypk, txfee, 3, isRemote) > 0) - { - if ((inputs = AddTokenCCInputs(cp, mtx, tokenaddr, tokenid, total, 60, useMempool)) > 0) - { - if (inputs < total) { - CCerror = strprintf("insufficient token inputs"); - return NullUniValue; - } - - uint8_t destEvalCode = EVAL_TOKENS; - if (cp->additionalTokensEvalcode2 != 0) - destEvalCode = cp->additionalTokensEvalcode2; // this is NFT - - // check if it is NFT - //if (vopretNonfungible.size() > 0) - // destEvalCode = vopretNonfungible.begin()[0]; - - if (inputs > total) - CCchange = (inputs - total); - mtx.vout.push_back(CTxOut(total, spk)); - if (CCchange != 0) - mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, CCchange, mypk)); - - // add optional probe conds to non-usual sign vins - for (auto p : probeconds) - CCAddVintxCond(cp, p.first, p.second); - - UniValue sigData = FinalizeCCTxExt(isRemote, 0, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutPubkeys, std::make_pair((uint8_t)0, vscript_t()))); - if (!ResultHasTx(sigData)) - CCerror = "could not finalize tx"; - return sigData; - } - else { - CCerror = strprintf("no token inputs"); - } - } - else - { - CCerror = "insufficient normal inputs for tx fee"; - } - return NullUniValue; -} -*/ - -int64_t GetTokenBalance(CPubKey pk, uint256 tokenid, bool usemempool) +int64_t GetTokenBalance(CPubKey pk, uint256 tokenid) { uint256 hashBlock; CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction tokentx; - uint256 tokenidInOpret; - std::vector pks; - std::vector oprets; // CCerror = strprintf("obsolete, cannot return correct value without eval"); // return 0; if (myGetTransaction(tokenid, tokentx, hashBlock) == 0) { - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "cant find tokenid" << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "cant find tokenid" << std::endl); CCerror = strprintf("cant find tokenid"); return 0; } - uint8_t funcid = DecodeTokenOpRetV1(tokentx.vout.back().scriptPubKey, tokenidInOpret, pks, oprets); - if (tokentx.vout.size() < 2 || !IsTokenCreateFuncid(funcid)) - { - CCerror = strprintf("not a tokenid (invalid tokenbase)"); - return 0; - } - struct CCcontract_info *cp, C; cp = CCinit(&C, EVAL_TOKENS); return(AddTokenCCInputs(cp, mtx, pk, tokenid, 0, 0)); @@ -1571,17 +942,16 @@ UniValue TokenInfo(uint256 tokenid) uint256 hashBlock; CTransaction tokenbaseTx; std::vector origpubkey; - std::vector oprets; + std::vector> oprets; vscript_t vopretNonfungible; std::string name, description; - uint8_t version; - struct CCcontract_info *cpTokens, tokensCCinfo; + cpTokens = CCinit(&tokensCCinfo, EVAL_TOKENS); if( !myGetTransaction(tokenid, tokenbaseTx, hashBlock) ) { - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "cant find tokenid" << std::endl); + fprintf(stderr, "TokenInfo() cant find tokenid\n"); result.push_back(Pair("result", "error")); result.push_back(Pair("error", "cant find tokenid")); return(result); @@ -1592,10 +962,9 @@ UniValue TokenInfo(uint256 tokenid) return(result); } - uint8_t funcid = DecodeTokenCreateOpRetV1(tokenbaseTx.vout.back().scriptPubKey, origpubkey, name, description, oprets); - if (tokenbaseTx.vout.size() > 0 && !IsTokenCreateFuncid(funcid)) + if (tokenbaseTx.vout.size() > 0 && DecodeTokenCreateOpRet(tokenbaseTx.vout[tokenbaseTx.vout.size() - 1].scriptPubKey, origpubkey, name, description, oprets) != 'c') { - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "passed tokenid isnt token creation txid" << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenInfo() passed tokenid isnt token creation txid" << std::endl); result.push_back(Pair("result", "error")); result.push_back(Pair("error", "tokenid isnt token creation txid")); return result; @@ -1606,15 +975,13 @@ UniValue TokenInfo(uint256 tokenid) result.push_back(Pair("name", name)); int64_t supply = 0, output; - for (int v = 0; v < tokenbaseTx.vout.size(); v++) + for (int v = 0; v < tokenbaseTx.vout.size() - 1; v++) if ((output = IsTokensvout(false, true, cpTokens, NULL, tokenbaseTx, v, tokenid)) > 0) supply += output; result.push_back(Pair("supply", supply)); result.push_back(Pair("description", description)); - //GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); - if (oprets.size() > 0) - vopretNonfungible = oprets[0]; + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); if( !vopretNonfungible.empty() ) result.push_back(Pair("data", HexStr(vopretNonfungible))); @@ -1669,18 +1036,18 @@ UniValue TokenList() auto addTokenId = [&](uint256 txid) { if (myGetTransaction(txid, vintx, hashBlock) != 0) { - if (vintx.vout.size() > 0 && DecodeTokenCreateOpRetV1(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0) { + if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0) { result.push_back(txid.GetHex()); } } }; - SetCCtxids(txids, cp->normaladdr, false, cp->evalcode, 0, zeroid, 'c'); // find by old normal addr marker + SetCCtxids(txids, cp->normaladdr,false,cp->evalcode,zeroid,'c'); // find by old normal addr marker for (std::vector::const_iterator it = txids.begin(); it != txids.end(); it++) { addTokenId(*it); } - SetCCunspents(addressIndexCCMarker, cp->unspendableCCaddr, true); // find by burnable validated cc addr marker + SetCCunspents(addressIndexCCMarker, cp->unspendableCCaddr,true); // find by burnable validated cc addr marker for (std::vector >::const_iterator it = addressIndexCCMarker.begin(); it != addressIndexCCMarker.end(); it++) { addTokenId(it->first.txhash); } diff --git a/src/cc/CCtokens.h b/src/cc/CCtokens.h index 2bb69dfa8bc..3705a8f6db2 100644 --- a/src/cc/CCtokens.h +++ b/src/cc/CCtokens.h @@ -25,154 +25,17 @@ #include "CCinclude.h" -/// Returns non-fungible data of token if this is a NFT -/// @param tokenid id of token -/// @param vopretNonfungible non-fungible token data. The first byte is the evalcode of the contract that validates the NFT-data -void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible); - // CCcustom bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); -bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, std::string &errorStr); -std::string CreateTokenLocal(int64_t txfee, int64_t tokensupply, std::string name, std::string description, vscript_t nonfungibleData); -UniValue CreateTokenExt(const CPubKey &remotepk, int64_t txfee, int64_t tokensupply, std::string name, std::string description, vscript_t nonfungibleData, uint8_t markerEvalCode, bool addTxInMemory); -std::string TokenTransfer(int64_t txfee, uint256 assetid, CPubKey destpk, int64_t total); -UniValue TokenTransferExt(const CPubKey &remotepk, int64_t txfee, uint256 tokenid, const char *tokenaddr, std::vector> probeconds, std::vector destpubkeys, int64_t total, bool useMempool = false); -//UniValue TokenTransferSpk(const CPubKey &remotepk, int64_t txfee, uint256 tokenid, const char *tokenaddr, std::vector> probeconds, const CScript &spk, int64_t total, const std::vector &voutPubkeys, bool useMempool = false); -CAmount HasBurnedTokensvouts(const CTransaction& tx, uint256 reftokenid); +bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid); +std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description, std::vector nonfungibleData); +std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector destpubkey, int64_t total); +int64_t HasBurnedTokensvouts(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, uint256 reftokenid); CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey); bool IsTokenMarkerVout(CTxOut vout); -int64_t GetTokenBalance(CPubKey pk, uint256 tokenid, bool usemempool = false); +int64_t GetTokenBalance(CPubKey pk, uint256 tokenid); UniValue TokenInfo(uint256 tokenid); UniValue TokenList(); -UniValue TokenBeginTransferTx(CMutableTransaction &mtx, struct CCcontract_info *cp, const CPubKey &remotepk, CAmount txfee); -UniValue TokenAddTransferVout(CMutableTransaction &mtx, struct CCcontract_info *cp, const CPubKey &remotepk, uint256 tokenid, const char *tokenaddr, std::vector destpubkeys, const std::pair &probecond, CAmount amount, bool useMempool); -UniValue TokenFinalizeTransferTx(CMutableTransaction &mtx, struct CCcontract_info *cp, const CPubKey &remotepk, CAmount txfee, const CScript &opret); - -/// Adds token inputs to transaction object. If tokenid is a non-fungible token then the function will set additionalTokensEvalcode2 variable in the cp object to the eval code from NFT data to spend NFT outputs properly -/// @param cp CCcontract_info structure -/// @param mtx mutable transaction object -/// @param pk pubkey for whose token inputs to add -/// @param tokenid id of token which inputs to add -/// @param total amount to add (if total==0 no inputs are added and all available amount is returned) -/// @param maxinputs maximum number of inputs to add. If 0 then CC_MAXVINS define is used -//int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const CPubKey &pk, uint256 tokenid, int64_t total, int32_t maxinputs, bool useMempool = false); - -/// @private overload used in kogs -//int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, char *tokenaddr, uint256 tokenid, int64_t total, int32_t maxinputs, vscript_t &vopretNonfungible); - -/// An overload that also returns NFT data in vopretNonfungible parameter -/// the rest parameters are the same as in the first AddTokenCCInputs overload -/// @see AddTokenCCInputs -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const char *tokenaddr, uint256 tokenid, int64_t total, int32_t maxinputs, bool useMempool = false); - -/// @private overload used in old cc -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const CPubKey &pk, uint256 tokenid, int64_t total, int32_t maxinputs); - -/// @private overload used in old assets -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const CPubKey &pk, uint256 tokenid, int64_t total, int32_t maxinputs, vscript_t &vopretNonfungible); - -/// Checks if a transaction vout is true token vout, for this check pubkeys and eval code in token opreturn are used to recreate vout and compare it with the checked vout. -/// Verifies that the transaction total token inputs value equals to total token outputs (that is, token balance is not changed in this transaction) -/// @param goDeeper also recursively checks the previous token transactions (or the creation transaction) and ensures token balance is not changed for them too -/// @param checkPubkeys always true -/// @param cp CCcontract_info structure initialized for EVAL_TOKENS eval code -/// @param eval could be NULL, if not NULL then the eval parameter is used to report validation error -/// @param tx transaction object to check -/// @param v vout number (starting from 0) -/// @param reftokenid id of the token. The vout is checked if it has this tokenid -/// @returns true if vout is true token with the reftokenid id -int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid); - -/// Creates a token cryptocondition that allows to spend it by one key -/// The resulting cc will have two eval codes (EVAL_TOKENS and evalcode parameter value). -/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param pk pubkey to spend the cc -/// @returns cryptocondition object. Must be disposed with cc_free function when not used any more -CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk); - -/// Overloaded function that creates a token cryptocondition that allows to spend it by one key -/// The resulting cc will have two eval codes (EVAL_TOKENS and evalcode parameter value). -/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param evalcode2 yet another cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param pk pubkey to spend the cc -/// @returns cryptocondition object. Must be disposed with cc_free function when not used any more -CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk); - -/// Creates new 1of2 token cryptocondition that allows to spend it by either of two keys -/// Resulting vout will have three eval codes (EVAL_TOKENS, evalcode and evalcode2 parameter values). -/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param pk1 one of two pubkeys to spend the cc -/// @param pk2 second of two pubkeys to spend the cc -/// @returns cryptocondition object. Must be disposed with cc_free function when not used any more -CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2); - -/// Creates new 1of2 token cryptocondition that allows to spend it by either of two keys -/// The resulting cc will have two eval codes (EVAL_TOKENS and evalcode parameter value). -/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param evalcode2 yet another cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param pk1 one of two pubkeys to spend the cc -/// @param pk2 second of two pubkeys to spend the cc -/// @returns cryptocondition object. Must be disposed with cc_free function when not used any more -CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2); - -/// Creates a token transaction output with a cryptocondition that allows to spend it by one key. -/// The resulting vout will have two eval codes (EVAL_TOKENS and evalcode parameter value). -/// The returned output should be added to a transaction vout array. -/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param nValue value of the output in satoshi -/// @param pk pubkey to spend the cc -/// @returns vout object -/// @see CCinit -/// @see CCcontract_info -CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk, std::vector>* vData = nullptr); - -/// Another MakeTokensCC1vout overloaded function that creates a token transaction output with a cryptocondition with two eval codes that allows to spend it by one key. -/// Resulting vout will have three eval codes (EVAL_TOKENS, evalcode and evalcode2 parameter values). -/// The returned output should be added to a transaction vout array. -/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param evalcode2 yet another cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param nValue value of the output in satoshi -/// @param pk pubkey to spend the cc -/// @returns vout object -/// @see CCinit -/// @see CCcontract_info -CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk, std::vector>* vData = nullptr); - -/// MakeTokensCC1of2vout creates a token transaction output with a 1of2 cryptocondition that allows to spend it by either of two keys. -/// The resulting vout will have two eval codes (EVAL_TOKENS and evalcode parameter value). -/// The returned output should be added to a transaction vout array. -/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param nValue value of the output in satoshi -/// @param pk1 one of two pubkeys to spend the cc -/// @param pk2 second of two pubkeys to spend the cc -/// @returns vout object -/// @see CCinit -/// @see CCcontract_info -CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector>* vData = nullptr); - -/// Another overload of MakeTokensCC1of2vout creates a token transaction output with a 1of2 cryptocondition with two eval codes that allows to spend it by either of two keys. -/// The resulting vout will have three eval codes (EVAL_TOKENS, evalcode and evalcode2 parameter values). -/// The returned output should be added to a transaction vout array. -/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param evalcode2 yet another cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code) -/// @param nValue value of the output in satoshi -/// @param pk1 one of two pubkeys to spend the cc -/// @param pk2 second of two pubkeys to spend the cc -/// @returns vout object -/// @see CCinit -/// @see CCcontract_info -CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector>* vData = nullptr); - -inline bool IsTokenCreateFuncid(uint8_t funcid) { return funcid == 'c'; } -inline bool IsTokenTransferFuncid(uint8_t funcid) { return funcid == 't'; } -bool MyGetCCopretV2(const CScript &scriptPubKey, CScript &opret); - -bool IsEqualVouts(const CTxOut &v1, const CTxOut &v2); - -bool TokensIsVer1Active(const Eval *eval); - -const char cctokens_log[] = "cctokens"; - #endif diff --git a/src/cc/CCtokenutils.cpp b/src/cc/CCtokenutils.cpp index b22b45f1bf6..73209bcb5c9 100644 --- a/src/cc/CCtokenutils.cpp +++ b/src/cc/CCtokenutils.cpp @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright � 2014-2019 The SuperNET Developers. * +* Copyright © 2014-2019 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -18,287 +18,260 @@ // This code was moved to a separate source file to enable linking libcommon.so (with importcoin.cpp which depends on some token functions) #include "CCtokens.h" -#include "old/CCtokens_v0.h" - #ifndef IS_CHARINSTR #define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) #endif -#ifndef MAY2020_NNELECTION_HARDFORK -#define MAY2020_NNELECTION_HARDFORK 1590926400 -#endif +// NOTE: this inital tx won't be used by other contract +// for tokens to be used there should be at least one 't' tx with other contract's custom opret +CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, vscript_t vopretNonfungible) +{ + /* CScript opret; + uint8_t evalcode = EVAL_TOKENS; + funcid = 'c'; // override the param + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; \ + if (!vopretNonfungible.empty()) { + ss << (uint8_t)OPRETID_NONFUNGIBLEDATA; + ss << vopretNonfungible; + }); */ -// return true if new v1 version activation time is passed or chain is always works v1 -// return false if v0 is still active -bool TokensIsVer1Active(const Eval *eval) -{ - static const char *chains_only_version1[] = { - // "RFOXLIKE", - // "DIMXY11", - // "DIMXY14", "DIMXY14_2" - }; - - bool isTimev1 = true; - if (eval == NULL) { - // std::cerr << __func__ << " komodo_currentheight()=" << komodo_currentheight() << " GetLatestTimestamp(komodo_currentheight())=" << GetLatestTimestamp(komodo_currentheight()) << std::endl; - if (GetLatestTimestamp(komodo_currentheight()) < MAY2020_NNELECTION_HARDFORK) - isTimev1 = false; - } - else { - // std::cerr << __func__ << " eval->GetCurrentHeight()=" << eval->GetCurrentHeight() << " GetLatestTimestamp(eval->GetCurrentHeight())=" << GetLatestTimestamp(eval->GetCurrentHeight()) << std::endl; - if (GetLatestTimestamp(eval->GetCurrentHeight()) < MAY2020_NNELECTION_HARDFORK) - isTimev1 = false; - } - for (auto const name : chains_only_version1) - if (strcmp(name, ASSETCHAINS_SYMBOL) == 0) - return true; - return isTimev1; -} + std::vector> oprets; -// compatibility code -// adds old-style opretid -// for create oprets treat EVAL_IMPORTCOIN as import tx -static std::vector> CreationOpretsToOpretsWithId(const std::vector &oprets) { - std::vector> opretswithid; - - for (auto const &o : oprets) { - if (o.size() > 0) { - uint8_t opretid = 0; - switch(o[0]) { - case EVAL_IMPORTCOIN: - opretid = tokensv0::OPRETID_IMPORTDATA; - break; - default: - opretid = tokensv0::OPRETID_NONFUNGIBLEDATA; - break; - } - if (opretid != 0) - opretswithid.push_back(std::make_pair(opretid, o)); - } - } - return opretswithid; + if(!vopretNonfungible.empty()) + oprets.push_back(std::make_pair(OPRETID_NONFUNGIBLEDATA, vopretNonfungible)); + return EncodeTokenCreateOpRet(funcid, origpubkey, name, description, oprets); } -// compatibility code -// adds old-style opretid for eval code -// for non create oprets treat EVAL_IMPORTCOIN as burn tx -static std::vector> NonCreationOpretsToOpretsWithId(const std::vector &oprets) { - std::vector> opretswithid; +CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector> oprets) +{ + CScript opret; + uint8_t evalcode = EVAL_TOKENS; + funcid = 'c'; // override the param - for (auto const &o : oprets) - { - if (o.size() > 0) { - uint8_t opretid = 0; - switch(o[0]) { - case EVAL_CHANNELS: - opretid = tokensv0::OPRETID_CHANNELSDATA; - break; - case EVAL_HEIR: - opretid = tokensv0::OPRETID_HEIRDATA; - break; - case 17: - opretid = tokensv0::OPRETID_ROGUEGAMEDATA; - break; - case EVAL_ASSETS: - opretid = tokensv0::OPRETID_ASSETSDATA; - break; - case EVAL_PEGS: - opretid = tokensv0::OPRETID_PEGSDATA; - break; - case EVAL_GATEWAYS: - opretid = tokensv0::OPRETID_GATEWAYSDATA; - break; - case EVAL_IMPORTCOIN: - opretid = tokensv0::OPRETID_BURNDATA; - break; - } - if (opretid != 0) - opretswithid.push_back(std::make_pair(opretid, o)); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; + for (auto o : oprets) { + if (o.first != 0) { + ss << (uint8_t)o.first; + ss << o.second; } - } - return opretswithid; + }); + return(opret); } -CScript EncodeTokenCreateOpRetV1(const std::vector &origpubkey, const std::string &name, const std::string &description, const std::vector &oprets) -{ - // call compatibility code: - if (!TokensIsVer1Active(NULL)) { - return tokensv0::EncodeTokenCreateOpRet('c', origpubkey, name, description, CreationOpretsToOpretsWithId(oprets)); // route to the previous version - } - +/* +// opret 'i' for imported tokens +CScript EncodeTokenImportOpRet(std::vector origpubkey, std::string name, std::string description, uint256 srctokenid, std::vector> oprets) +{ CScript opret; uint8_t evalcode = EVAL_TOKENS; - uint8_t funcid = 'C'; // 'C' indicates v1 - uint8_t version = 1; + uint8_t funcid = 'i'; + + srctokenid = revuint256(srctokenid); // do not forget this - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << version << origpubkey << name << description; - for (const auto &o : oprets) { - ss << o; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description << srctokenid; + for (auto o : oprets) { + if (o.first != 0) { + ss << (uint8_t)o.first; + ss << o.second; + } }); return(opret); } +*/ + -/* CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::pair opretWithId) { std::vector> oprets; oprets.push_back(opretWithId); return EncodeTokenOpRet(tokenid, voutPubkeys, oprets); -}*/ +} -// v2 format with no opretid (evalcode is used instead) -CScript EncodeTokenOpRetV1(uint256 tokenid, const std::vector &voutPubkeys, const std::vector &oprets) +CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::vector> oprets) { - // call compatibility code: - if (!TokensIsVer1Active(NULL)) { - return tokensv0::EncodeTokenOpRet(tokenid, voutPubkeys, NonCreationOpretsToOpretsWithId(oprets)); // route to the previous version - } - CScript opret; - uint8_t tokenFuncId = 'T'; // 'T' indicates v1 + uint8_t tokenFuncId = 't'; uint8_t evalCodeInOpret = EVAL_TOKENS; - uint8_t version = 1; tokenid = revuint256(tokenid); - uint8_t pkCount = voutPubkeys.size(); - if (voutPubkeys.size() > 2) - { - pkCount = 2; - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << "EncodeTokenOpRet voutPubkeys.size()=" << voutPubkeys.size() << " not supported" << std::endl); + uint8_t ccType = 0; + if (voutPubkeys.size() >= 0 && voutPubkeys.size() <= 2) + ccType = voutPubkeys.size(); + else { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "EncodeTokenOpRet voutPubkeys.size()=" << voutPubkeys.size() << " not supported" << std::endl); } //vopret_t vpayload; //GetOpReturnData(payload, vpayload); - opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << version << tokenid << pkCount; - if (pkCount >= 1) ss << voutPubkeys[0]; - if (pkCount == 2) ss << voutPubkeys[1]; - for (const auto &o : oprets) { - ss << o; - }); + opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; + if (ccType >= 1) ss << voutPubkeys[0]; + if (ccType == 2) ss << voutPubkeys[1]; + for (auto o : oprets) { + if (o.first != 0) { + ss << (uint8_t)o.first; + ss << o.second; + } + }); + + // bad opret cases (tries to attach payload without re-serialization): + + // error "64: scriptpubkey": + // if (payload.size() > 0) + // opret += payload; + + // error "64: scriptpubkey": + // CScript opretPayloadNoOpcode(vpayload); + // return opret + opretPayloadNoOpcode; + + // error "sig_aborted": + // opret.resize(opret.size() + vpayload.size()); + // CScript::iterator it = opret.begin() + opret.size(); + // for (int i = 0; i < vpayload.size(); i++, it++) + // *it = vpayload[i]; return opret; } +// overload for compatibility +//CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector voutPubkeys, CScript payload) +//{ +// return EncodeTokenOpRet(tokenid, voutPubkeys, payload); +//} // overload for fungible tokens (no additional data in opret): -uint8_t DecodeTokenCreateOpRetV1(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description) -{ - std::vector opretsDummy; - return DecodeTokenCreateOpRetV1(scriptPubKey, origpubkey, name, description, opretsDummy); +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description) { + //vopret_t vopretNonfungibleDummy; + std::vector> opretsDummy; + return DecodeTokenCreateOpRet(scriptPubKey, origpubkey, name, description, opretsDummy); } -uint8_t DecodeTokenCreateOpRetV1(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector &oprets) +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector> &oprets) { vscript_t vopret, vblob; - uint8_t dummyEvalcode, funcid, version; + uint8_t dummyEvalcode, funcid, opretId = 0; + GetOpReturnData(scriptPubKey, vopret); oprets.clear(); - // try to decode old version: - std::vector> opretswithid; - if ((funcid = tokensv0::DecodeTokenCreateOpRet(scriptPubKey, origpubkey, name, description, opretswithid)) != 0) // check pubkey is parsed okay - { - for (auto const & oi : opretswithid) - oprets.push_back(oi.second); - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "decoded v0 opret funcid=" << (char)funcid << " name=" << name << std::endl); - return funcid; - } - - - GetOpReturnData(scriptPubKey, vopret); - if (vopret.size() > 2 && vopret[0] == EVAL_TOKENS && vopret[1] == 'C') + if (vopret.size() > 2 && vopret.begin()[0] == EVAL_TOKENS && vopret.begin()[1] == 'c') { - if (E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> version; ss >> origpubkey; ss >> name; ss >> description; - while (!ss.eof()) { + if (E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description; + while (!ss.eof()) { + ss >> opretId; + if (!ss.eof()) { ss >> vblob; - oprets.push_back(vblob); // put oprets - })) + oprets.push_back(std::make_pair(opretId, vblob)); + } + })) { - return 'c'; // convert to old-style funcid - } + return(funcid); + } } - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "incorrect token create opret" << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenCreateOpRet() incorrect token create opret" << std::endl); return (uint8_t)0; } // decode token opret: // for 't' returns all data from opret, vopretExtra contains other contract's data (currently only assets'). // for 'c' returns only funcid. NOTE: nonfungible data is not returned -uint8_t DecodeTokenOpRetV1(const CScript scriptPubKey, uint256 &tokenid, std::vector &voutPubkeys, std::vector &oprets) +uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector> &oprets) { - vscript_t vopret, vblob, vorigPubkey, vnonfungibleDummy; - uint8_t funcId = 0, evalCode, dummyEvalCode, evalCodeOld, dummyFuncId, pkCount, version; + vscript_t vopret, vblob, dummyPubkey, vnonfungibleDummy; + uint8_t funcId = 0, *script, dummyEvalCode, dummyFuncId, ccType, opretId = 0; std::string dummyName; std::string dummyDescription; uint256 dummySrcTokenId; CPubKey voutPubkey1, voutPubkey2; - oprets.clear(); - - // try to decode old opreturn version (check tokenid is not null): - std::vector> opretswithid; - if ((funcId = tokensv0::DecodeTokenOpRet(scriptPubKey, evalCodeOld, tokenid, voutPubkeys, opretswithid)) != 0) - { - for (auto const & oi : opretswithid) - oprets.push_back(oi.second); - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "decoded v0 opret funcid=" << (char)funcId << " tokenid=" << tokenid.GetHex() << std::endl); - return funcId; - } + vscript_t voldstyledata; + bool foundOldstyle = false; GetOpReturnData(scriptPubKey, vopret); - // tokenid = zeroid; this was incorrect: cleared the passed tokenid if creation tx + script = (uint8_t *)vopret.data(); + tokenid = zeroid; + oprets.clear(); - if (vopret.size() > 2) + if (script != NULL && vopret.size() > 2) { - voutPubkeys.clear(); - evalCode = vopret[0]; - if (evalCode != EVAL_TOKENS) { - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "incorrect evalcode in tokens opret" << std::endl); + evalCodeTokens = script[0]; + if (evalCodeTokens != EVAL_TOKENS) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() incorrect evalcode in tokens opret" << std::endl); return (uint8_t)0; } - funcId = vopret[1]; - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG2, stream << "decoded funcId=" << (char)(funcId ? funcId : ' ') << std::endl); + funcId = script[1]; + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "DecodeTokenOpRet() decoded funcId=" << (char)(funcId ? funcId : ' ') << std::endl); switch (funcId) { - case 'C': - funcId = DecodeTokenCreateOpRetV1(scriptPubKey, vorigPubkey, dummyName, dummyDescription, oprets); - if (funcId != 0) { - // add orig pubkey - voutPubkeys.push_back(pubkey2pk(vorigPubkey)); - } - return funcId; // should be converted to old-style funcid - - case 'T': - if (E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> version; ss >> tokenid; ss >> pkCount; - if (pkCount >= 1) ss >> voutPubkey1; - if (pkCount >= 2) ss >> voutPubkey2; // pkCountshould not be > 2 + case 'c': + return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, oprets); + + case 't': + + // compatibility with old-style rogue or assets data (with no opretid): + // try to unmarshal old-style rogue or assets data: + foundOldstyle = E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; + if (ccType >= 1) ss >> voutPubkey1; + if (ccType == 2) ss >> voutPubkey2; + if (!ss.eof()) { + ss >> voldstyledata; + }) && voldstyledata.size() >= 2 && + (voldstyledata.begin()[0] == 0x11 /*EVAL_ROGUE*/ && IS_CHARINSTR(voldstyledata.begin()[1], "RHQKG") || + voldstyledata.begin()[0] == EVAL_ASSETS && IS_CHARINSTR(voldstyledata.begin()[1], "sbSBxo")) ; + + if (foundOldstyle || // fix for compatibility with old style data (no opretid) + E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; + if (ccType >= 1) ss >> voutPubkey1; + if (ccType == 2) ss >> voutPubkey2; while (!ss.eof()) { - ss >> vblob; - oprets.push_back(vblob); + ss >> opretId; + if (!ss.eof()) { + ss >> vblob; + oprets.push_back(std::make_pair(opretId, vblob)); + } })) { - tokenid = revuint256(tokenid); + if (!(ccType >= 0 && ccType <= 2)) { //incorrect ccType + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() incorrect ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); + return (uint8_t)0; + } + + // add verification pubkeys: + voutPubkeys.clear(); if (voutPubkey1.IsValid()) voutPubkeys.push_back(voutPubkey1); if (voutPubkey2.IsValid()) voutPubkeys.push_back(voutPubkey2); - return 't'; // convert to old style funcid + + tokenid = revuint256(tokenid); + + if (foundOldstyle) { //patch for old-style opret data with no opretid + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet() found old-style rogue/asset data, evalcode=" << (int)voldstyledata.begin()[0] << " funcid=" << (char)voldstyledata.begin()[1] << " for tokenid=" << revuint256(tokenid).GetHex() << std::endl); + uint8_t opretIdRestored; + if (voldstyledata.begin()[0] == 0x11 /*EVAL_ROGUE*/) + opretIdRestored = OPRETID_ROGUEGAMEDATA; + else // EVAL_ASSETS + opretIdRestored = OPRETID_ASSETSDATA; + + oprets.push_back(std::make_pair(opretIdRestored, voldstyledata)); + } + + return(funcId); } - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "bad opret format for 'T'," << " pkCount=" << (int)pkCount << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() bad opret format," << " ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); return (uint8_t)0; - + default: - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "illegal funcid=" << (int)funcId << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() illegal funcid=" << (int)funcId << std::endl); return (uint8_t)0; } } else { - LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "empty opret, could not parse" << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() empty opret, could not parse" << std::endl); } return (uint8_t)0; } @@ -349,46 +322,30 @@ CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) { } // make three-eval (token+evalcode+evalcode2) 1of2 cc vout: -CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector>* vData) +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2) { CTxOut vout; CC *payoutCond = MakeTokensCCcond1of2(evalcode, evalcode2, pk1, pk2); vout = CTxOut(nValue, CCPubKey(payoutCond)); - if (vData) - { - //std::vector> vtmpData = std::vector>(vData->begin(), vData->end()); - std::vector vPubKeys = std::vector(); - //vPubKeys.push_back(pk); // Warning: if add a pubkey here, the Solver function will add it to vSolutions and ExtractDestination might use it to get the spk address (such result might not be expected) - COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 1, vPubKeys, (*vData)); - vout.scriptPubKey << ccp.AsVector() << OP_DROP; - } cc_free(payoutCond); return(vout); } // overload to make two-eval (token+evalcode) 1of2 cc vout: -CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector>* vData) { - return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2, vData); +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) { + return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2); } // make three-eval (token+evalcode+evalcode2) cc vout: -CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk, std::vector>* vData) +CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk) { CTxOut vout; CC *payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk); vout = CTxOut(nValue, CCPubKey(payoutCond)); - if (vData) - { - //std::vector> vtmpData = std::vector>(vData->begin(), vData->end()); - std::vector vPubKeys = std::vector(); - //vPubKeys.push_back(pk); // Warning: if add a pubkey here, the Solver function will add it to vSolutions and ExtractDestination might use it to get the spk address (such result might not be expected) - COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 1, vPubKeys, (*vData)); - vout.scriptPubKey << ccp.AsVector() << OP_DROP; - } cc_free(payoutCond); return(vout); } // overload to make two-eval (token+evalcode) cc vout: -CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk, std::vector>* vData) { - return MakeTokensCC1vout(evalcode, 0, nValue, pk, vData); +CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) { + return MakeTokensCC1vout(evalcode, 0, nValue, pk); } diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index aed84c78d10..d06fe4a067b 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -14,7 +14,6 @@ ******************************************************************************/ #include "CCinclude.h" -#include "CCtokens.h" #include "key_io.h" std::vector NULL_pubkeys; @@ -58,8 +57,7 @@ UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *c int32_t i,flag,mgret,utxovout,n,err = 0; char myaddr[64], destaddr[64], unspendable[64], mytokensaddr[64], mysingletokensaddr[64], unspendabletokensaddr[64],CC1of2CCaddr[64]; uint8_t *privkey = NULL, myprivkey[32] = { '\0' }, unspendablepriv[32] = { '\0' }, /*tokensunspendablepriv[32],*/ *msg32 = 0; - CC *mycond = 0, *othercond = 0, *othercond2 = 0, *othercond4 = 0, *othercond3 = 0, *othercond1of2 = NULL, *othercond1of2tokens = NULL, *cond = 0, *condCC2 = 0, - *mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL, *vectcond = NULL; + CC *mycond=0, *othercond=0, *othercond2=0,*othercond4=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond=0, *condCC2=0,*mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL; CPubKey unspendablepk /*, tokensunspendablepk*/; struct CCcontract_info *cpTokens, tokensC; UniValue sigData(UniValue::VARR),result(UniValue::VOBJ); @@ -104,7 +102,7 @@ UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *c // to spend from dual/three-eval mypk vout GetTokensCCaddress(cp, mytokensaddr, mypk); // NOTE: if additionalEvalcode2 is not set it is a dual-eval (not three-eval) cc cond: - mytokenscond = MakeTokensCCcond1(cp->evalcode, cp->evalcodeNFT, mypk); + mytokenscond = MakeTokensCCcond1(cp->evalcode, cp->additionalTokensEvalcode2, mypk); // to spend from single-eval EVAL_TOKENS mypk cpTokens = CCinit(&tokensC, EVAL_TOKENS); @@ -113,7 +111,7 @@ UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *c // to spend from dual/three-eval EVAL_TOKEN+evalcode 'unspendable' pk: GetTokensCCaddress(cp, unspendabletokensaddr, unspendablepk); // it may be a three-eval cc, if cp->additionalEvalcode2 is set - othertokenscond = MakeTokensCCcond1(cp->evalcode, cp->evalcodeNFT, unspendablepk); + othertokenscond = MakeTokensCCcond1(cp->evalcode, cp->additionalTokensEvalcode2, unspendablepk); //Reorder vins so that for multiple normal vins all other except vin0 goes to the end //This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation. @@ -166,7 +164,7 @@ UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *c nmask = (1LL << n) - 1; if ( 0 && (mask & nmask) != (CCmask & nmask) ) fprintf(stderr,"mask.%llx vs CCmask.%llx %llx %llx %llx\n",(long long)(mask & nmask),(long long)(CCmask & nmask),(long long)mask,(long long)CCmask,(long long)nmask); - if ( totalinputs >= totaloutputs+txfee ) + if ( totalinputs >= totaloutputs+2*txfee ) { change = totalinputs - (totaloutputs+txfee); mtx.vout.push_back(CTxOut(change,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); @@ -288,7 +286,7 @@ UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *c if (othercond1of2tokens == 0) // NOTE: if additionalEvalcode2 is not set then it is dual-eval cc else three-eval cc // TODO: verify evalcodes order if additionalEvalcode2 is not 0 - othercond1of2tokens = MakeTokensCCcond1of2(cp->evalcode, cp->evalcodeNFT, cp->tokens1of2pk[0], cp->tokens1of2pk[1]); + othercond1of2tokens = MakeTokensCCcond1of2(cp->evalcode, cp->additionalTokensEvalcode2, cp->tokens1of2pk[0], cp->tokens1of2pk[1]); cond = othercond1of2tokens; } else @@ -310,33 +308,7 @@ UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *c } } //else privkey = myprivkey; - if (flag == 0) - { - const uint8_t nullpriv[32] = {'\0'}; - // use vector of dest addresses and conds to probe vintxconds - for (auto &t : cp->CCvintxprobes) { - char coinaddr[64]; - - if (vectcond != NULL) - cc_free(vectcond); // free prev used cond - vectcond = t.CCwrapped.getCC(); // Note: need to cc_free at the function exit - if (vectcond != NULL) { - Getscriptaddress(coinaddr, CCPubKey(vectcond)); - // std::cerr << __func__ << " destaddr=" << destaddr << " coinaddr=" << coinaddr << std::endl; - if (strcmp(destaddr, coinaddr) == 0) { - if (memcmp(t.CCpriv, nullpriv, sizeof(t.CCpriv) / sizeof(t.CCpriv[0])) != 0) - privkey = t.CCpriv; - else - privkey = myprivkey; - flag = 1; - cond = vectcond; - break; - } - } - } - } - - if (flag == 0) + if ( flag == 0 ) { fprintf(stderr,"CC signing error: vini.%d has unknown CC address.(%s)\n",i,destaddr); memset(myprivkey,0,32); @@ -344,7 +316,7 @@ UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *c } } uint256 sighash = SignatureHash(CCPubKey(cond), mtx, i, SIGHASH_ALL,utxovalues[i],consensusBranchId, &txdata); - if ( false ) + if ( 0 ) { int32_t z; for (z=0; z<32; z++) @@ -408,9 +380,6 @@ UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *c if ( othertokenscond != 0 ) cc_free(othertokenscond); memset(myprivkey,0,sizeof(myprivkey)); - if (vectcond != NULL) - cc_free(vectcond); - std::string strHex = EncodeHexTx(mtx); if ( strHex.size() > 0 ) result.push_back(Pair(JSON_HEXTX, strHex)); @@ -473,7 +442,7 @@ void SetCCtxids(std::vector > &addressIndex } } -void SetCCtxids(std::vector &txids,char *coinaddr,bool ccflag, uint8_t evalcode, int64_t amount, uint256 filtertxid, uint8_t func) +void SetCCtxids(std::vector &txids,char *coinaddr,bool ccflag, uint8_t evalcode, uint256 filtertxid, uint8_t func) { int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector > addresses; std::vector > addressIndex; @@ -497,7 +466,7 @@ void SetCCtxids(std::vector &txids,char *coinaddr,bool ccflag, uint8_t return; for (std::vector >::const_iterator it1=addressIndex.begin(); it1!=addressIndex.end(); it1++) { - if ((amount==0 && it1->second>=0) || (amount>0 && it1->second==amount)) txids.push_back(it1->first.txhash); + if (it1->second>=0) txids.push_back(it1->first.txhash); } } } @@ -577,8 +546,7 @@ int64_t CCfullsupply(uint256 tokenid) uint256 hashBlock; int32_t numvouts; CTransaction tx; std::vector origpubkey; std::string name,description; if ( myGetTransaction(tokenid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) { - std::vector oprets; - if (DecodeTokenCreateOpRetV1(tx.vout[numvouts-1].scriptPubKey,origpubkey,name,description,oprets)) + if (DecodeTokenCreateOpRet(tx.vout[numvouts-1].scriptPubKey,origpubkey,name,description)) { return(tx.vout[1].nValue); } @@ -586,12 +554,12 @@ int64_t CCfullsupply(uint256 tokenid) return(0); } -// TODO: remove this func or add IsTokenVout check (in other places just AddTokenCCInputs is used instead, maybe make it to do the job here) int64_t CCtoken_balance(char *coinaddr,uint256 reftokenid) { int64_t price,sum = 0; int32_t numvouts; CTransaction tx; uint256 tokenid,txid,hashBlock; std::vector vopretExtra; std::vector > unspentOutputs; + uint8_t evalCode; SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) @@ -601,8 +569,8 @@ int64_t CCtoken_balance(char *coinaddr,uint256 reftokenid) { char str[65]; std::vector voutTokenPubkeys; - std::vector oprets; - if ( reftokenid==txid || (DecodeTokenOpRetV1(tx.vout[numvouts-1].scriptPubKey, tokenid, voutTokenPubkeys, oprets) != 0 && reftokenid == tokenid)) + std::vector> oprets; + if ( reftokenid==txid || (DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCode, tokenid, voutTokenPubkeys, oprets) != 0 && reftokenid == tokenid)) { sum += it->second.satoshis; } @@ -611,7 +579,6 @@ int64_t CCtoken_balance(char *coinaddr,uint256 reftokenid) return(sum); } -// finds two utxo indexes that are closest to the passed value from below or above: int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *belowp,struct CC_utxo utxos[],int32_t numunspents,int64_t value) { int32_t i,abovei,belowi; int64_t above,below,gap,atx_value; @@ -690,7 +657,7 @@ int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total sum = 0; BOOST_FOREACH(const COutput& out, vecOutputs) { - if ( out.fSpendable != 0 && (vecOutputs.size() < maxinputs || out.tx->vout[out.i].nValue >= threshold) ) + if ( out.fSpendable != 0 && out.tx->vout[out.i].nValue >= threshold ) { txid = out.tx->GetHash(); vout = out.i; @@ -794,13 +761,11 @@ int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t to { txid = it->first.txhash; vout = (int32_t)it->first.index; - //if ( it->second.satoshis < threshold ) - // continue; - if( it->second.satoshis == 0 ) + if ( it->second.satoshis < threshold ) continue; if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 ) { - //fprintf(stderr,"check %.8f to vins array.%d of %d %s/v%d\n",(double)tx.vout[vout].nValue/COIN,n,maxinputs,txid.GetHex().c_str(),(int32_t)vout); + //fprintf(stderr,"check %.8f to vins array.%d of %d %s/v%d\n",(double)out.tx->vout[out.i].nValue/COIN,n,maxutxos,txid.GetHex().c_str(),(int32_t)vout); if ( mtx.vin.size() > 0 ) { for (i=0; inValue = it->second.satoshis; up->vout = vout; sum += up->nValue; - //fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxinputs); + //fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxutxos); if ( n >= maxinputs || sum >= total ) break; } @@ -885,4 +850,4 @@ void AddSigData2UniValue(UniValue &sigdata, int32_t vini, UniValue& ccjson, std: elem.push_back(Pair("scriptPubKey", sscriptpubkey)); elem.push_back(Pair("amount", amount)); sigdata.push_back(elem); -} +} \ No newline at end of file diff --git a/src/cc/CCutilbits.cpp b/src/cc/CCutilbits.cpp index e9dc8b1ece5..72fd0f2664e 100644 --- a/src/cc/CCutilbits.cpp +++ b/src/cc/CCutilbits.cpp @@ -95,55 +95,21 @@ CPubKey buf2pk(uint8_t *buf33) CPubKey pubkey2pk(std::vector vpubkey) { - CPubKey pk; + CPubKey pk; pk.Set(vpubkey.begin(), vpubkey.end()); - return pk; + return(pk); } -// checks if category and level is enabled in -debug param -// like -debug=cctokens (CCLOG_INFO) or -debug=cctokens-2 (CCLOG_DEBUG2 and lower levels) -static bool cc_log_accept_category(const char *category, int level) +void CCLogPrintStr(const char *category, int level, const std::string &str) { if (level < 0) - return true; // always print errors - + level = 0; if (level > CCLOG_MAXLEVEL) level = CCLOG_MAXLEVEL; for (int i = level; i <= CCLOG_MAXLEVEL; i++) if (LogAcceptCategory((std::string(category) + std::string("-") + std::to_string(i)).c_str()) || // '-debug=cctokens-0', '-debug=cctokens-1',... - i == CCLOG_INFO && LogAcceptCategory(std::string(category).c_str())) { // also supporting '-debug=cctokens' for CCLOG_INFO - return true; + i == 0 && LogAcceptCategory(std::string(category).c_str())) { // also supporting '-debug=cctokens' for CCLOG_INFO + LogPrintStr(str); + break; } - return false; -} - -void CCLogPrintStr(const char *category, int level, const std::string &str) -{ - if (cc_log_accept_category(category, level)) - LogPrintStr(str); -} - -void CCLogPrintF(const char *category, int level, const char *format, ...) -{ - char logstr[2048]; - - if (cc_log_accept_category(category, level)) { - va_list args; - va_start(args, format); - vsnprintf(logstr, sizeof(logstr), format, args); - logstr[sizeof(logstr) - 1] = '\0'; - LogPrintStr(logstr); - va_end(args); - } } - -thread_local bool is_remote_rpc_call; -void SetRemoteRPCCall(bool isRemote) -{ - is_remote_rpc_call = isRemote; -} - -bool IsRemoteRPCCall() -{ - return is_remote_rpc_call; -} \ No newline at end of file diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 643a077105f..bd79eaa6857 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -18,7 +18,6 @@ */ #include "CCinclude.h" -#include "CCtokens.h" #include "komodo_structs.h" #include "key_io.h" @@ -98,37 +97,36 @@ bool makeCCopret(CScript &opret, std::vector> &vData) return true; } -CTxOut MakeCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk, std::vector>* vData) +CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue, CPubKey pk, std::vector>* vData) { CTxOut vout; - CC *payoutCond = MakeCCcond1(evalcode, pk); - vout = CTxOut(nValue, CCPubKey(payoutCond)); - if (vData) + CC *payoutCond = MakeCCcond1(evalcode,pk); + vout = CTxOut(nValue,CCPubKey(payoutCond)); + if ( vData ) { //std::vector> vtmpData = std::vector>(vData->begin(), vData->end()); std::vector vPubKeys = std::vector(); - //vPubKeys.push_back(pk); // Warning: if add a pubkey here, the Solver function will add it to vSolutions and ExtractDestination might use it to get the spk address (such result might not be expected) - COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 1, vPubKeys, (*vData)); + //vPubKeys.push_back(pk); + COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 1, vPubKeys, ( * vData)); vout.scriptPubKey << ccp.AsVector() << OP_DROP; } cc_free(payoutCond); return(vout); } -CTxOut MakeCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector>* vData) +CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2, std::vector>* vData) { CTxOut vout; - CC *payoutCond = MakeCCcond1of2(evalcode, pk1, pk2); - vout = CTxOut(nValue, CCPubKey(payoutCond)); - if (vData) + CC *payoutCond = MakeCCcond1of2(evalcode,pk1,pk2); + vout = CTxOut(nValue,CCPubKey(payoutCond)); + if ( vData ) { //std::vector> vtmpData = std::vector>(vData->begin(), vData->end()); std::vector vPubKeys = std::vector(); - // skip pubkeys. These need to maybe be optional and we need some way to get them out that is easy! - // this is for multisig - //vPubKeys.push_back(pk1); // Warning: if add a pubkey here, the Solver function will add it to vSolutions and ExtractDestination might use it to get the spk address (such result might not be expected) + // skip pubkeys. These need to maybe be optional and we need some way to get them out that is easy! + //vPubKeys.push_back(pk1); //vPubKeys.push_back(pk2); - COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 2, vPubKeys, (*vData)); + COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 2, vPubKeys, ( * vData)); vout.scriptPubKey << ccp.AsVector() << OP_DROP; } cc_free(payoutCond); @@ -141,10 +139,8 @@ CC* GetCryptoCondition(CScript const& scriptSig) opcodetype opcode; std::vector ffbin; if (scriptSig.GetOp(pc, opcode, ffbin)) - if (ffbin.data() != NULL) // could return NULL if called for coinbase - return cc_readFulfillmentBinary((uint8_t*)ffbin.data(), ffbin.size()-1); - - return(NULL); + return cc_readFulfillmentBinary((uint8_t*)ffbin.data(), ffbin.size()-1); + else return(0); } bool IsCCInput(CScript const& scriptSig) @@ -296,42 +292,10 @@ CPubKey CCtxidaddr(char *txidaddr,uint256 txid) buf33[0] = 0x02; endiancpy(&buf33[1],(uint8_t *)&txid,32); pk = buf2pk(buf33); - if (txidaddr != NULL) - Getscriptaddress(txidaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); + Getscriptaddress(txidaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); return(pk); } -// CCtxidaddr version that makes valid pubkey by tweaking it -CPubKey CCtxidaddr_tweak(char *txidaddr, uint256 txid) -{ - uint8_t buf33[33]; - CPubKey pk; - - buf33[0] = 0x02; - endiancpy(&buf33[1], (uint8_t *)&txid, 32); - - // tweak last byte - // NOTE: this algorithm should not be changed, it should remain compatible with existing in chains txid-pubkeys - int maxtweaks = 256; - while (maxtweaks--) { - pk = buf2pk(buf33); - if (pk.IsFullyValid()) - break; - buf33[sizeof(buf33)-1]++; - } - - if (pk.IsFullyValid()) { - if (txidaddr != NULL) - Getscriptaddress(txidaddr, CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); - return(pk); - } - else { - if (txidaddr != NULL) - strcpy(txidaddr, ""); - return CPubKey(); - } -} - CPubKey CCCustomtxidaddr(char *txidaddr,uint256 txid,uint8_t taddr,uint8_t prefix,uint8_t prefix2) { uint8_t buf33[33]; CPubKey pk; @@ -380,7 +344,7 @@ bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk) destaddr[0] = 0; if (pk.size() == 0) pk = GetUnspendable(cp, 0); - return(_GetTokensCCaddress(destaddr, cp->evalcode, cp->evalcodeNFT, pk)); + return(_GetTokensCCaddress(destaddr, cp->evalcode, cp->additionalTokensEvalcode2, pk)); } @@ -400,7 +364,7 @@ bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey { CC *payoutCond; destaddr[0] = 0; - if ((payoutCond = MakeTokensCCcond1of2(cp->evalcode, cp->evalcodeNFT, pk, pk2)) != 0) // if additionalTokensEvalcode2 not set then it is dual-eval cc else three-eval cc + if ((payoutCond = MakeTokensCCcond1of2(cp->evalcode, cp->additionalTokensEvalcode2, pk, pk2)) != 0) // if additionalTokensEvalcode2 not set then it is dual-eval cc else three-eval cc { Getscriptaddress(destaddr, CCPubKey(payoutCond)); cc_free(payoutCond); @@ -408,7 +372,6 @@ bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey return(destaddr[0] != 0); } -// validate cc or normal vout address and value bool ConstrainVout(CTxOut vout, int32_t CCflag, char *cmpaddr, int64_t nValue) { char destaddr[64]; @@ -490,23 +453,20 @@ bool Myprivkey(uint8_t myprivkey[]) char coinaddr[64],checkaddr[64]; std::string strAddress; char *dest; int32_t i,n; CBitcoinAddress address; CKeyID keyID; CKey vchSecret; uint8_t buf33[33]; if ( KOMODO_NSPV_SUPERLITE ) { - if ( NSPV_logintime != 0 && time(NULL) <= NSPV_logintime+NSPV_AUTOLOGOUT ) - { - vchSecret = DecodeSecret(NSPV_wifstr); - memcpy(myprivkey,vchSecret.begin(),32); - //for (i=0; i<32; i++) - // fprintf(stderr,"%02x",myprivkey[i]); - //fprintf(stderr," myprivkey %s\n",NSPV_wifstr); - memset((uint8_t *)vchSecret.begin(),0,32); - return true; - } - else if ( KOMODO_DEX_P2P == 0 ) + if ( NSPV_logintime == 0 || time(NULL) > NSPV_logintime+NSPV_AUTOLOGOUT ) { fprintf(stderr,"need to be logged in to get myprivkey\n"); return false; } + vchSecret = DecodeSecret(NSPV_wifstr); + memcpy(myprivkey,vchSecret.begin(),32); + //for (i=0; i<32; i++) + // fprintf(stderr,"%02x",myprivkey[i]); + //fprintf(stderr," myprivkey %s\n",NSPV_wifstr); + memset((uint8_t *)vchSecret.begin(),0,32); + return true; } - if ( pwalletMain != 0 && Getscriptaddress(coinaddr,CScript() << Mypubkey() << OP_CHECKSIG) != 0 ) + if ( Getscriptaddress(coinaddr,CScript() << Mypubkey() << OP_CHECKSIG) != 0 ) { n = (int32_t)strlen(coinaddr); strAddress.resize(n+1); @@ -534,22 +494,9 @@ bool Myprivkey(uint8_t myprivkey[]) else printf("mismatched privkey -> addr %s vs %s\n",checkaddr,coinaddr); } return(false); - } else fprintf(stderr,"(%p) cant find (%s) privkey\n",pwalletMain,coinaddr); + } #endif - } else fprintf(stderr,"cant find (%s) in wallet\n",coinaddr); - } - if ( KOMODO_DEX_P2P != 0 ) - { - static int32_t onetimeflag; static uint8_t sessionpriv[32]; - if ( onetimeflag == 0 ) - { - void OS_randombytes(unsigned char *x,long xlen); - OS_randombytes(sessionpriv,32); - fprintf(stderr,"privkey for pubkey not found -> generate session specific privkey\n"); - onetimeflag = 1; } - memcpy(myprivkey,sessionpriv,32); - return(true); } fprintf(stderr,"privkey for the -pubkey= address is not in the wallet, importprivkey!\n"); return(false); @@ -639,37 +586,19 @@ uint256 CCOraclesReverseScan(char const *logcategory,uint256 &txid,int32_t heigh return(zeroid); } -int64_t CCOraclesGetDepositBalance(char const *logcategory,uint256 reforacletxid,uint256 batontxid) -{ - CTransaction tx; uint256 hash,prevbatontxid,hashBlock,oracletxid; int32_t len,len2,numvouts; - int64_t val,balance=0; CPubKey pk; std::vectordata; - - if ( myGetTransaction(batontxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) - { - if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,prevbatontxid,pk,data) == 'D' && oracletxid == reforacletxid ) - { - if ( oracle_format(&hash,&balance,0,'L',(uint8_t *)data.data(),(int32_t)(sizeof(int32_t)+sizeof(uint256)*2),(int32_t)data.size()) == (int32_t)(sizeof(int32_t)+sizeof(uint256)*2+sizeof(int64_t))) - { - return (balance); - } - } - } - return (0); -} - int32_t NSPV_coinaddr_inmempool(char const *logcategory,char *coinaddr,uint8_t CCflag); -int32_t myIs_coinaddr_inmempoolvout(char const *logcategory,uint256 txid,char *coinaddr) +int32_t myIs_coinaddr_inmempoolvout(char const *logcategory,char *coinaddr) { int32_t i,n; char destaddr[64]; if ( KOMODO_NSPV_SUPERLITE ) - return(NSPV_coinaddr_inmempool(logcategory,coinaddr,0)); + return(NSPV_coinaddr_inmempool(logcategory,coinaddr,1)); BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) { const CTransaction &tx = e.GetTx(); if ( (n= tx.vout.size()) > 0 ) { - if (txid == tx.GetHash()) continue; + const uint256 &txid = tx.GetHash(); for (i=0; i &txs,uint8_t evalcode,uint8_ return(i); } -int32_t CCCointxidExists(char const *logcategory,uint256 txid, uint256 cointxid) +int32_t CCCointxidExists(char const *logcategory,uint256 cointxid) { char txidaddr[64]; std::string coin; int32_t numvouts; uint256 hashBlock; std::vector > addressIndex; CCtxidaddr(txidaddr,cointxid); - SetCCtxids(addressIndex,txidaddr,false); + SetCCtxids(addressIndex,txidaddr,true); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { return(-1); } - return(myIs_coinaddr_inmempoolvout(logcategory,txid,txidaddr)); -} - -bool CompareHexVouts(std::string hex1, std::string hex2) -{ - CTransaction tx1,tx2; - - if (!DecodeHexTx(tx1,hex1)) return (false); - if (!DecodeHexTx(tx2,hex2)) return (false); - if (tx1.vout.size()!=tx2.vout.size()) return (false); - for (int i=0;i<(int32_t)tx1.vout.size();i++) if (tx1.vout[i]!=tx2.vout[i]) return (false); - return (true); -} - -bool CheckVinPk(const CTransaction &tx, int32_t n, std::vector &pubkeys) -{ - CTransaction vintx; uint256 blockHash; char destaddr[64],pkaddr[64]; - - if(myGetTransaction(tx.vin[n].prevout.hash, vintx, blockHash)==0) return (false); - if( tx.vin[n].prevout.n < vintx.vout.size() && Getscriptaddress(destaddr, vintx.vout[tx.vin[n].prevout.n].scriptPubKey) != 0 ) - { - for(int i=0;i<(int32_t)pubkeys.size();i++) - { - pubkey2addr(pkaddr, (uint8_t *)pubkeys[i].begin()); - if (strcmp(pkaddr, destaddr) == 0) { - return (true); - } - } - } - return (false); + return(myIs_coinaddr_inmempoolvout(logcategory,txidaddr)); } /* Get the block merkle root for a proof @@ -765,18 +665,6 @@ uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::ve return merkleBlock.txn.ExtractMatches(txids); } -int64_t komodo_get_blocktime(uint256 hashBlock) -{ - BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end() && (*mi).second) - { - CBlockIndex* pindex = (*mi).second; - if (chainActive.Contains(pindex)) - return pindex->GetBlockTime(); - } - return 0; -} - extern struct NSPV_inforesp NSPV_inforesult; int32_t komodo_get_current_height() { @@ -787,16 +675,15 @@ int32_t komodo_get_current_height() else return chainActive.LastTip()->GetHeight(); } -bool komodo_txnotarizedconfirmed(uint256 txid, int32_t minconfirms) +bool komodo_txnotarizedconfirmed(uint256 txid) { char str[65]; - int32_t confirms,minimumconfirms,notarized=0,txheight=0,currentheight=0;; + int32_t confirms,notarized=0,txheight=0,currentheight=0;; CTransaction tx; uint256 hashBlock; CBlockIndex *pindex; char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; struct komodo_state *sp; - if (minconfirms==0) return (true); if ( KOMODO_NSPV_SUPERLITE ) { if ( NSPV_myGetTransaction(txid,tx,hashBlock,txheight,currentheight) == 0 ) @@ -840,15 +727,14 @@ bool komodo_txnotarizedconfirmed(uint256 txid, int32_t minconfirms) } confirms=1 + pindex->GetHeight() - txheight; } - if (minconfirms>1) minimumconfirms=minconfirms; - else minimumconfirms=MIN_NON_NOTARIZED_CONFIRMS; + if ((sp= komodo_stateptr(symbol,dest)) != 0 && (notarized=sp->NOTARIZED_HEIGHT) > 0 && txheight > sp->NOTARIZED_HEIGHT) notarized=0; #ifdef TESTMODE notarized=0; #endif //TESTMODE if (notarized>0 && confirms > 1) return (true); - else if (notarized==0 && confirms >= minimumconfirms) + else if (notarized==0 && confirms >= MIN_NON_NOTARIZED_CONFIRMS) return (true); return (false); } @@ -1012,173 +898,3 @@ bool CClib_Dispatch(const CC *cond,Eval *eval,std::vector paramsNull,co } return eval->Invalid("cclib CC must have evalcode between 16 and 127"); } - -void OS_randombytes(unsigned char *x,long xlen); -extern bits256 curve25519_basepoint9(); - -int32_t _SuperNET_cipher(uint8_t nonce[crypto_box_NONCEBYTES],uint8_t *cipher,uint8_t *message,int32_t len,bits256 destpub,bits256 srcpriv,uint8_t *buf) -{ - memset(cipher,0,len+crypto_box_ZEROBYTES); - memset(buf,0,crypto_box_ZEROBYTES); - memcpy(buf+crypto_box_ZEROBYTES,message,len); - if ( crypto_box(cipher,buf,len+crypto_box_ZEROBYTES,nonce,destpub.bytes,srcpriv.bytes) != 0 ) - return(-1); - return(len + crypto_box_ZEROBYTES); -} - -uint8_t *_SuperNET_decipher(uint8_t nonce[crypto_box_NONCEBYTES],uint8_t *cipher,uint8_t *message,int32_t len,bits256 srcpub,bits256 mypriv) -{ - int32_t err; - if ( (0) ) - { - int32_t z; - for (z=0; z sizeof(space) ) - buf = (uint8_t *)calloc(1,allocsize); - else - { - memset(space,0,sizeof(space)); - buf = space; - } - cipher = (uint8_t *)calloc(1,allocsize); - *ptrp = cipher; - origptr = nonce = cipher; - mypubkey = curve25519(privkey,curve25519_basepoint9()); - memcpy(cipher,mypubkey.bytes,sizeof(mypubkey)); - nonce = &cipher[sizeof(mypubkey)]; - OS_randombytes(nonce,crypto_box_NONCEBYTES); - cipher = &nonce[crypto_box_NONCEBYTES]; - _SuperNET_cipher(nonce,cipher,data,datalen,destpubkey,privkey,buf); - if ( (0) ) - { - int32_t z; - uint8_t message[8192]; - for (z=0; z<32; z++) - fprintf(stderr,"%02x",mypubkey.bytes[z]); - fprintf(stderr," mypub\n"); - if ( _SuperNET_decipher(nonce,cipher,message,datalen+crypto_box_ZEROBYTES,destpubkey,privkey) != 0 ) - { - for (z=0; zCCvintxprobes.push_back(ccprobe); -} diff --git a/src/cc/CCvalidation.cpp b/src/cc/CCvalidation.cpp deleted file mode 100644 index 398efcde549..00000000000 --- a/src/cc/CCvalidation.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2020 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -/* - CCValidaton has common validation functions that should be used to validate some basic things in all CCs. - */ - -#include "CCinclude.h" - -bool FetchCCtx(uint256 txid, CTransaction& tx, struct CCcontract_info *cp) -{ - EvalRef eval; uint256 hashBlock; - if (myGetTransaction(txid,tx,hashBlock)==0) return (false); - return (ValidateCCtx(tx,cp)); -} - -bool ValidateCCtx(const CTransaction& tx, struct CCcontract_info *cp) -{ - EvalRef eval; - if (cp->validate(cp,eval.get(),tx,0)) return (true); - return (false); -} - -bool ExactAmounts(Eval* eval, const CTransaction &tx, uint64_t txfee) -{ - CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0; - - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - for (i=0; iInvalid("ExactAmounts - cannot find tx for vin."+std::to_string(i)); - inputs += vinTx.vout[tx.vin[i].prevout.n].nValue; - } - for (i=0; iInvalid("invalid total amounts - inputs != outputs + txfee!"); - return (true); -} - -//no_burn - every OP_RETURN vout must not have >0 nValue, -//no_multi - transaction cannot have multiple OP_RETURN vouts, -//last_vout - no OP_RETURN vout is valid anywhere except vout[-1] -bool CCOpretCheck(Eval* eval, const CTransaction &tx, bool no_burn, bool no_multi, bool last_vout) -{ - int count=0,i=0; - int numvouts = tx.vout.size(); - - for (i=0;iInvalid("invalid OP_RETURN vout, its value must be 0!"); - if ( last_vout && i != numvouts-1 ) return eval->Invalid("invalid OP_RETURN vout, it must be the last vout in tx!"); - } - } - if (count == 0) return eval->Invalid("invalid CC tx, no OP_RETURN vout!"); - if ( no_multi && count > 1) return eval->Invalid("multiple OP_RETURN vouts are not allowed in single tx!"); - return true; -} diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp index c8b4125cf74..9ae4cc1eb40 100644 --- a/src/cc/assets.cpp +++ b/src/cc/assets.cpp @@ -17,8 +17,6 @@ #include "CCtokens.h" /* - TODO: update: - Assets can be created or transferred. native coins are also locked in the EVAL_ASSETS address, so we need a strict rule on when utxo in the special address are native coins and when they are assets. The specific rule that must not be violated is that vout0 for 'b'/'B' funcid are native coins. All other utxo locked in the special address are assets. @@ -127,94 +125,133 @@ + // tx validation -static bool AssetsValidateInternal(struct CCcontract_info *cpAssets, Eval* eval,const CTransaction &tx, uint32_t nIn) +bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransaction &tx, uint32_t nIn) { static uint256 zero; CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, assetid, assetid2; - int32_t numvins, numvouts, preventCCvins, preventCCvouts; - int64_t unit_price, vin_unit_price; - std::vector origpubkey, vinorigpubkey, vopretNonfungible; + int32_t i,starti, numvins, numvouts, preventCCvins, preventCCvouts; + int64_t remaining_price, nValue, assetoshis, outputsDummy,inputs,tmpprice,totalunits,ignore; + std::vector origpubkey, tmporigpubkey, ignorepubkey, vopretNonfungible, vopretNonfungibleDummy; uint8_t funcid, evalCodeInOpret; char destaddr[64], origNormalAddr[64], origTokensCCaddr[64], origCCaddrDummy[64]; char tokensDualEvalUnspendableCCaddr[64], origAssetsCCaddr[64]; - CAmount tokens = 0; - CAmount assetoshis = 0; + //return true; numvins = tx.vin.size(); numvouts = tx.vout.size(); + outputsDummy = inputs = 0; preventCCvins = preventCCvouts = -1; // add specific chains exceptions for old token support: if (strcmp(ASSETCHAINS_SYMBOL, "SEC") == 0 && chainActive.Height() <= 144073) return true; + if (strcmp(ASSETCHAINS_SYMBOL, "MGNX") == 0 && chainActive.Height() <= 210190) + return true; + + // add specific chains exceptions for old token support: + if (strcmp(ASSETCHAINS_SYMBOL, "SEC") == 0 && chainActive.Height() <= 144073) + return true; + if (strcmp(ASSETCHAINS_SYMBOL, "MGNX") == 0 && chainActive.Height() <= 210190) return true; if (numvouts == 0) return eval->Invalid("AssetValidate: no vouts"); - if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, unit_price, origpubkey)) == 0 ) + if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 ) return eval->Invalid("AssetValidate: invalid opreturn payload"); // non-fungible tokens support: GetNonfungibleData(assetid, vopretNonfungible); if (vopretNonfungible.size() > 0) - cpAssets->evalcodeNFT = vopretNonfungible.begin()[0]; + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; - // find dual-eval tokens global addr where tokens are locked: + // find dual-eval tokens unspendable addr: GetTokensCCaddress(cpAssets, tokensDualEvalUnspendableCCaddr, GetUnspendable(cpAssets, NULL)); - // originator cc address, this is for marker validation: + // this is for marker validation: GetCCaddress(cpAssets, origAssetsCCaddr, origpubkey); // we need this for validating single-eval tokens' vins/vous: struct CCcontract_info *cpTokens, tokensC; cpTokens = CCinit(&tokensC, EVAL_TOKENS); - //fprintf(stderr,"AssetValidate (%c)\n",funcid); + // find single-eval token user cc addr: + //GetCCaddress(cpTokens, signleEvalTokensCCaddr, pubkey2pk(origpubkey)); - // we do not need createTx (it is checked in cc tokens) - //if( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid, createTx, hashBlock) == 0 ) - // return eval->Invalid("cant find asset create txid"); - //else if( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2, createTx, hashBlock) == 0 ) - // return eval->Invalid("cant find asset2 create txid"); - // else + //fprintf(stderr,"AssetValidate (%c)\n",funcid); - if( IsCCInput(tx.vin[0].scriptSig) != 0 ) // vin0 should be normal vin + if( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid, createTx, hashBlock) == 0 ) + return eval->Invalid("cant find asset create txid"); + else if( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2, createTx, hashBlock) == 0 ) + return eval->Invalid("cant find asset2 create txid"); + else if( IsCCInput(tx.vin[0].scriptSig) != 0 ) // vin0 should be normal vin return eval->Invalid("illegal asset vin0"); else if( numvouts < 2 ) return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below - - if (assetid == zeroid) - return eval->Invalid("illegal assetid"); - //if (!AssetGetCCInputs(cpAssets, tokensInputs, assetsInputs, eval, tx, assetid)) // set tokensInputs and assetsInputs - // return false; // returns false if some problems with reading vintxes - + else if( funcid != 'c' ) + { + /* if( funcid == 't' ) + starti = 0; + else + starti = 1; */ + + if( assetid == zero ) + return eval->Invalid("illegal assetid"); + + else if (!AssetCalcAmounts(cpAssets, inputs, outputsDummy/*outputsDummy is calculated incorrectly but not used*/, eval, tx, assetid)) { // Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs + return false; // returns false if some problems with reading vintxes + } + } switch( funcid ) { - case 'c': - // see token cc + case 'c': // create wont be called to be verified as it has no CC inputs + //vin.0: normal input + //vout.0: issuance assetoshis to CC + //vout.1: normal output for change (if any) + //vout.n-1: opreturn [EVAL_ASSETS] ['c'] [{"":""}] + //if (evalCodeInOpret == EVAL_ASSETS) + // return eval->Invalid("unexpected AssetValidate for createasset"); + // return return eval->Invalid("invalid asset funcid \'c\'"); - - case 't': - // see token cc + break; + case 't': // transfer + //vin.0: normal input + //vin.1 .. vin.n-1: valid CC outputs + //vout.0 to n-2: assetoshis output to CC + //vout.n-2: normal output for change (if any) + //vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid] + //if (inputs == 0) + // return eval->Invalid("no asset inputs for transfer"); + //fprintf(stderr,"transfer preliminarily validated %.8f -> %.8f (%d %d)\n",(double)inputs/COIN,(double)outputs/COIN,preventCCvins,preventCCvouts); return eval->Invalid("invalid asset funcid \'t\'"); - + break; + case 'b': // buyoffer //vins.*: normal inputs (bid + change) - //vout.0: amount of bid to cc assets global address + //vout.0: amount of bid to unspendable //vout.1: CC output for marker //vout.2: normal output for change (if any) - //vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey] + // vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey] - // as we don't use tokenconvert 'b' does not have cc inputs and we should not be here: + // as we don't use tokenconvert we should not be here: return eval->Invalid("invalid asset funcid (b)"); + if( remaining_price == 0 ) + return eval->Invalid("illegal null amount for buyoffer"); + else if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr,0) == 0 ) // coins to assets unspendable cc addr + return eval->Invalid("invalid vout for buyoffer"); + preventCCvins = 1; + preventCCvouts = 1; + fprintf(stderr,"buy offer validated to destaddr.(%s)\n",cpAssets->unspendableCCaddr); + break; + case 'o': // cancelbuy //vin.0: normal input //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0] @@ -223,13 +260,10 @@ static bool AssetsValidateInternal(struct CCcontract_info *cpAssets, Eval* eval, //vout.1: vin.2 back to users pubkey //vout.2: normal output for change (if any) //vout.n-1: opreturn [EVAL_ASSETS] ['o'] - if (assetoshis = AssetValidateBuyvin(cpAssets, eval, vin_unit_price, vinorigpubkey, origCCaddrDummy, origNormalAddr, tx, assetid) == 0) + if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, origCCaddrDummy, origNormalAddr, tx, assetid)) == 0 ) return(false); - else if( ConstrainVout(tx.vout[0], 0, origNormalAddr, assetoshis) == false ) + else if( ConstrainVout(tx.vout[0],0, origNormalAddr, nValue) == 0 ) return eval->Invalid("invalid refund for cancelbuy"); - else if( TotalPubkeyNormalInputs(tx, pubkey2pk(vinorigpubkey)) == 0 ) // check tx is signed by originator pubkey - return eval->Invalid("not the owner pubkey"); - preventCCvins = 3; preventCCvouts = 0; //fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origNormalAddr); @@ -237,9 +271,9 @@ static bool AssetsValidateInternal(struct CCcontract_info *cpAssets, Eval* eval, case 'B': // fillbuy: //vin.0: normal input - //vin.1: cc assets unspendable (vout.0 from buyoffer) buyTx.vout[0] + //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0] //vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue - //vout.0: remaining amount of bid to cc assets global address + //vout.0: remaining amount of bid to unspendable //vout.1: vin.1 value to signer of vin.2 //vout.2: vin.2 assetoshis to original pubkey //vout.3: CC output for assetoshis change (if any) @@ -247,47 +281,36 @@ static bool AssetsValidateInternal(struct CCcontract_info *cpAssets, Eval* eval, //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey] preventCCvouts = 4; - if( (assetoshis = AssetValidateBuyvin(cpAssets, eval, vin_unit_price, vinorigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) - return false; + if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) + return(false); else if( numvouts < 4 ) return eval->Invalid("not enough vouts for fillbuy"); - else if( vinorigpubkey != origpubkey ) // originator pk does not change in the new opret + else if( tmporigpubkey != origpubkey ) return eval->Invalid("mismatched origpubkeys for fillbuy"); - else if (unit_price != vin_unit_price) - return eval->Invalid("mismatched unit price for fillbuy"); else { - tokens = AssetsGetCCInputs(cpTokens, NULL, tx); - if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue ) // coins -> global cc address (remainder) + normal self address + if( nValue != tx.vout[0].nValue + tx.vout[1].nValue ) return eval->Invalid("locked value doesnt match vout0+1 fillbuy"); - else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != false ) // if token remainder exists + else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change present { - if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == false ) // tokens to originator cc addr (tokens+nonfungible evalcode) + if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals) return eval->Invalid("vout2 doesnt go to origpubkey fillbuy"); - else if( tokens != tx.vout[2].nValue + tx.vout[4].nValue ) // tokens from cc global address -> token global addr (remainder) + originator cc address - return eval->Invalid("tokens inputs doesnt match vout2+3 fillbuy"); + else if ( inputs != tx.vout[2].nValue + tx.vout[4].nValue ) + return eval->Invalid("asset inputs doesnt match vout2+3 fillbuy"); preventCCvouts ++; } - else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, tokens) == false ) // all tokens to originator cc addr, no cc change present - return eval->Invalid("vout2 doesnt match tokens inputs fillbuy"); - - if( ConstrainVout(tx.vout[1], 0, NULL, 0) == false ) // amount paid for tokens goes to normal addr (we can't check 'self' address) - return eval->Invalid("vout1 should be normal for fillbuy"); - else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, ASSETS_MARKER_AMOUNT) == false ) // marker to originator asset cc addr + else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals) + return eval->Invalid("vout2 doesnt match inputs fillbuy"); + else if( ConstrainVout(tx.vout[1], 0, NULL, 0) == 0 ) + return eval->Invalid("vout1 is CC for fillbuy"); + else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to asset cc addr return eval->Invalid("invalid marker for original pubkey"); - else if( ValidateBidRemainder(unit_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue) == false ) // check real price and coins spending from global addr + else if( ValidateBidRemainder(remaining_price, tx.vout[0].nValue, nValue, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) return eval->Invalid("mismatched remainder for fillbuy"); - else { - if( tx.vout[0].nValue / unit_price != 0 ) // remaining tokens to buy - { - if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == false ) // if remainder sufficient to buy tokens -> coins to asset global cc addr - return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy"); - } - else - { // remaining_units == 0 - if( ConstrainVout(tx.vout[0], 0, origNormalAddr, 0) == false ) // remainder less than token price should return to originator normal addr - return eval->Invalid("vout0 should be original normal address for fillbuy"); - } + else if( remaining_price != 0 ) + { + if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins to asset unspendable cc addr + return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy"); } } //fprintf(stderr,"fillbuy validated\n"); @@ -297,69 +320,84 @@ static bool AssetsValidateInternal(struct CCcontract_info *cpAssets, Eval* eval, case 's': // selloffer //vin.0: normal input //vin.1+: valid CC output for sale - //vout.0: vin.1 assetoshis output to CC assets global address - //vout.1: CC output to CC assets global address for marker + //vout.0: vin.1 assetoshis output to CC to unspendable + //vout.1: CC output for marker //vout.2: CC output for change (if any) //vout.3: normal output for change (if any) //'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey] //'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey] - // as we don't use tokenconvert 's' does not have cc inputs and we should not be here: + // as we don't use tokenconvert we should not be here: return eval->Invalid("invalid asset funcid (s)"); + + preventCCvouts = 2; + if( remaining_price == 0 ) + return eval->Invalid("illegal null remaining_price for selloffer"); + if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) + return eval->Invalid("invalid normal vout1 for sellvin"); + if( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change presents + { + preventCCvouts++; + if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, 0) == 0 ) // tokens to tokens unspendable cc addr. TODO: this in incorrect, should be assets if we got here! + return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); + else if( tx.vout[0].nValue + tx.vout[2].nValue != inputs ) + return eval->Invalid("mismatched vout0+vout2 total for selloffer"); + } + // no cc change: + else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // tokens to tokens unspendable cc addr TODO: this in incorrect, should be assets if got here! + return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); + //fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price); + break; case 'x': // cancel sell //vin.0: normal input - //vin.1: cc assets global address (vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx + //vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx //vin.2: CC marker from selloffer for txfee //vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey] //vout.1: vin.2 back to users pubkey //vout.2: normal output for change (if any) //vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid] - if( (tokens = AssetValidateSellvin(cpAssets, eval, vin_unit_price, vinorigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) // NOTE: - return false; - else if (ConstrainVout(tx.vout[0], 1, origTokensCCaddr, tokens) == false) // tokens returning to originator cc addr + if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) // NOTE: + return(false); + else if( ConstrainVout(tx.vout[0], 1, origTokensCCaddr, assetoshis) == 0 ) // tokens returning to originator cc addr return eval->Invalid("invalid vout for cancel"); - else if (TotalPubkeyNormalInputs(tx, pubkey2pk(vinorigpubkey)) == 0) // check tx is signed by originator pubkey - return eval->Invalid("not the owner pubkey"); preventCCvins = 3; preventCCvouts = 1; break; case 'S': // fillsell //vin.0: normal input - //vin.1: cc assets global address (vout.0 assetoshis from selloffer) sellTx.vout[0] + //vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0] //'S'.vin.2+: normal output that satisfies selloffer (*tx.vin[2])->nValue - //vout.0: remaining assetoshis -> cc assets global address + //vout.0: remaining assetoshis -> unspendable //vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any //'S'.vout.2: vin.2 value to original pubkey [origpubkey] //vout.3: normal output for change (if any) //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey] - if( (tokens = AssetValidateSellvin(cpAssets, eval, vin_unit_price, vinorigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) - return false; + if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) + return(false); else if( numvouts < 4 ) return eval->Invalid("not enough vouts for fillask"); - else if( vinorigpubkey != origpubkey ) + else if( tmporigpubkey != origpubkey ) return eval->Invalid("mismatched origpubkeys for fillask"); - else if (unit_price != vin_unit_price) - return eval->Invalid("mismatched unit price for fillask"); else { - if( tokens != tx.vout[0].nValue + tx.vout[1].nValue ) + if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue ) return eval->Invalid("locked value doesnt match vout0+1 fillask"); - if( ValidateAskRemainder(unit_price, tx.vout[0].nValue, tokens, tx.vout[1].nValue, tx.vout[2].nValue) == false ) + if( ValidateAskRemainder(remaining_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) return eval->Invalid("mismatched remainder for fillask"); - else if( ConstrainVout(tx.vout[1], 1, NULL, 0) == false ) // do not check tokens buyer's 'self' cc addr + else if( ConstrainVout(tx.vout[1], 1, NULL, 0) == 0 ) // do not check token buyer's cc addr return eval->Invalid("normal vout1 for fillask"); - else if( ConstrainVout(tx.vout[2], 0, origNormalAddr, 0) == false ) // coins to originator normal addr + else if( ConstrainVout(tx.vout[2], 0, origNormalAddr, 0) == 0 ) // coins to originator normal addr return eval->Invalid("normal vout1 for fillask"); - else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == false ) // marker to originator asset cc addr + else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to originator asset cc addr return eval->Invalid("invalid marker for original pubkey"); - else // if( remaining_units != 0 ) // remaining expected amount in coins, should be anyway + else if( remaining_price != 0 ) { - if( ConstrainVout(tx.vout[0], 1, tokensDualEvalUnspendableCCaddr, 0) == false ) // tokens remainder on global addr - return eval->Invalid("mismatched vout0 assets tokens dual unspendable CCaddr for fill sell"); + if ( ConstrainVout(tx.vout[0], 1, tokensDualEvalUnspendableCCaddr, 0) == 0 ) + return eval->Invalid("mismatched vout0 assets dual unspendable CCaddr for fill sell"); } } //fprintf(stderr,"fill validated\n"); @@ -382,47 +420,43 @@ static bool AssetsValidateInternal(struct CCcontract_info *cpAssets, Eval* eval, // eval->Invalid("asset2 inputs != outputs"); ////////// not implemented yet //////////// - if( (tokens = AssetValidateSellvin(cpTokens, eval, vin_unit_price, vinorigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) + if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) return(false); else if( numvouts < 3 ) return eval->Invalid("not enough vouts for fillex"); - else if( vinorigpubkey != origpubkey ) + else if( tmporigpubkey != origpubkey ) return eval->Invalid("mismatched origpubkeys for fillex"); - else if (unit_price != vin_unit_price) - return eval->Invalid("mismatched unit price for fillex"); else { - /* - if( tokens != tx.vout[0].nValue + tx.vout[1].nValue ) + if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue ) return eval->Invalid("locked value doesnt match vout0+1 fillex"); - else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != false ) + else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 ) ////////// not implemented yet //////////// { - if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == false ) + if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 ) return eval->Invalid("vout2 doesnt go to origpubkey fillex"); - else if( tokens != tx.vout[2].nValue + tx.vout[3].nValue ) + else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue ) { - fprintf(stderr,"inputs %.8f != %.8f + %.8f\n",(double)tokens/COIN,(double)tx.vout[2].nValue/COIN,(double)tx.vout[3].nValue/COIN); + fprintf(stderr,"inputs %.8f != %.8f + %.8f\n",(double)inputs/COIN,(double)tx.vout[2].nValue/COIN,(double)tx.vout[3].nValue/COIN); return eval->Invalid("asset inputs doesnt match vout2+3 fillex"); } } ////////// not implemented yet //////////// - else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, tokens) == false ) + else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 ) return eval->Invalid("vout2 doesnt match inputs fillex"); - else if( ConstrainVout(tx.vout[1], 0, 0, 0) == false ) + else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 ) return eval->Invalid("vout1 is CC for fillex"); - fprintf(stderr,"assets vout0 %lld, tokens %lld, vout2 %lld -> orig, vout1 %lld, total %lld\n", (long long)tx.vout[0].nValue, (long long)tokens,(long long)tx.vout[2].nValue,(long long)tx.vout[1].nValue,(long long)orig_units); - if( ValidateSwapRemainder(remaining_units, tx.vout[0].nValue, tokens, tx.vout[1].nValue, tx.vout[2].nValue, orig_units) == false ) - // return eval->Invalid("mismatched remainder for fillex"); - else if( ConstrainVout(tx.vout[1], 1, 0, 0) == false ) + fprintf(stderr,"assets vout0 %llu, vin1 %llu, vout2 %llu -> orig, vout1 %llu, total %llu\n",(long long)tx.vout[0].nValue,(long long)assetoshis,(long long)tx.vout[2].nValue,(long long)tx.vout[1].nValue,(long long)totalunits); + if( ValidateSwapRemainder(remaining_price, tx.vout[0].nValue, assetoshis,tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) + return eval->Invalid("mismatched remainder for fillex"); + else if( ConstrainVout(tx.vout[1], 1, 0, 0) == 0 ) ////////// not implemented yet //////////// return eval->Invalid("normal vout1 for fillex"); - else if( remaining_units != 0 ) + else if( remaining_price != 0 ) { - if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == false ) // TODO: unsure about this, but this is not impl yet anyway + if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == 0 ) // TODO: unsure about this, but this is not impl yet anyway return eval->Invalid("mismatched vout0 AssetsCCaddr for fillex"); } - */ } ////////// not implemented yet //////////// //fprintf(stderr,"fill validated\n"); @@ -434,24 +468,10 @@ static bool AssetsValidateInternal(struct CCcontract_info *cpAssets, Eval* eval, //break; } - bool bPrevent = PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts); // prevent presence of unknown cc vin or cc vouts in the tx + // what does this do? + bool bPrevent = PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts); // seems we do not need this call as we already checked vouts well //std::cerr << "AssetsValidate() PreventCC returned=" << bPrevent << std::endl; return (bPrevent); } -// redirect to AssetsValidateInternal and log error -bool AssetsValidate(struct CCcontract_info *cpAssets, Eval* eval,const CTransaction &tx, uint32_t nIn) -{ - if (!TokensIsVer1Active(NULL)) { - bool valid = tokensv0::AssetsValidate(cpAssets, eval, tx, nIn); // call assets validation version 0 - if (!valid) - LOGSTREAMFN(ccassets_log, CCLOG_ERROR, stream << "v0 validation error: " << eval->state.GetRejectReason() << ", code: " << eval->state.GetRejectCode() << ", tx: " << HexStr(E_MARSHAL(ss << tx)) << std::endl); - return valid; - } - if (!AssetsValidateInternal(cpAssets, eval, tx, nIn)) { - LOGSTREAMFN(ccassets_log, CCLOG_ERROR, stream << "validation error: " << eval->state.GetRejectReason() << ", code: " << eval->state.GetRejectCode() << ", tx: " << HexStr(E_MARSHAL(ss << tx)) << std::endl); - return false; - } - return true; -} diff --git a/src/cc/channels.cpp b/src/cc/channels.cpp index 3e8ca13d061..4d6299f4f6a 100644 --- a/src/cc/channels.cpp +++ b/src/cc/channels.cpp @@ -14,7 +14,6 @@ ******************************************************************************/ #include "CCchannels.h" -#include "CCtokens.h" /* The idea here is to allow instant (mempool) payments that are secured by dPoW. In order to simplify things, channels CC will require creating reserves for each payee locked in the destination user's CC address. This will look like the payment is already made, but it is locked until further released. The dPoW protection comes from the cancel channel having a delayed effect until the next notarization. This way, if a payment release is made and the chain reorged, the same payment release will still be valid when it is re-broadcast into the mempool. @@ -64,8 +63,6 @@ Possible third iteration: // start of consensus code #define CC_MARKER_VALUE 10000 -#define CHANNELCC_VERSION 1 -#define CC_TXFEE 10000 int64_t IsChannelsvout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey srcpub, CPubKey destpub,int32_t v) { @@ -94,32 +91,30 @@ int64_t IsChannelsMarkervout(struct CCcontract_info *cp,const CTransaction& tx,C return(0); } -CScript EncodeChannelsOpRet(uint8_t funcid,uint256 tokenid,uint256 opentxid,CPubKey srcpub,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 hashchain, uint8_t version, uint16_t confirmation) +CScript EncodeChannelsOpRet(uint8_t funcid,uint256 tokenid,uint256 opentxid,CPubKey srcpub,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 hashchain) { CScript opret; uint8_t evalcode = EVAL_CHANNELS; vscript_t vopret; - vopret = E_MARSHAL(ss << evalcode << funcid << opentxid << srcpub << destpub << numpayments << payment << hashchain << version << confirmation); + vopret = E_MARSHAL(ss << evalcode << funcid << opentxid << srcpub << destpub << numpayments << payment << hashchain); if (tokenid!=zeroid) { std::vector pks; pks.push_back(srcpub); pks.push_back(destpub); - return(EncodeTokenOpRetV1(tokenid,pks, { vopret })); + return(EncodeTokenOpRet(tokenid,pks, std::make_pair(OPRETID_CHANNELSDATA, vopret))); } opret << OP_RETURN << vopret; return(opret); } -uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey, uint256 &tokenid, uint256 &opentxid, CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain, uint8_t &version, uint16_t &confirmation) +uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey, uint256 &tokenid, uint256 &opentxid, CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain) { - std::vector oprets; - std::vector vopret, vOpretExtra; uint8_t *script,e,f; + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; - version=0; - confirmation=100; - if (DecodeTokenOpRetV1(scriptPubKey,tokenid,pubkeys,oprets)!=0 && GetOpReturnCCBlob(oprets, vOpretExtra) && vOpretExtra.size()>0) + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys,oprets)!=0 && GetOpretBlob(oprets, OPRETID_CHANNELSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) { vopret=vOpretExtra; } @@ -127,41 +122,54 @@ uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey, uint256 &tokenid, uint2 if ( vopret.size() > 2 ) { script = (uint8_t *)vopret.data(); - if ( script[0] == EVAL_CHANNELS && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> opentxid; ss >> srcpub; ss >> destpub; ss >> numpayments; ss >> payment; ss >> hashchain; ss >> version; ss >> confirmation) != 0 ) + if ( script[0] == EVAL_CHANNELS ) { - return(f); - } - else LOGSTREAM("channelscc",CCLOG_DEBUG2, stream << "script[0] " << script[0] << " != EVAL_CHANNELS" << std::endl); - } else LOGSTREAM("channelscc",CCLOG_DEBUG2, stream << "not enough opret.[" << vopret.size() << "]" << std::endl); + if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> opentxid; ss >> srcpub; ss >> destpub; ss >> numpayments; ss >> payment; ss >> hashchain) != 0 ) + { + return(f); + } + } else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "script[0] " << script[0] << " != EVAL_CHANNELS" << std::endl); + } else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "not enough opret.[" << vopret.size() << "]" << std::endl); return(0); } -bool ChannelsExactAmounts(Eval* eval,const CTransaction &tx) +bool ChannelsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) { uint256 txid,param3,tokenid; - CPubKey srcpub,destpub; uint16_t confirmation; - int32_t param1,numvouts; int64_t param2; uint8_t funcid,version; + CPubKey srcpub,destpub; + int32_t param1,numvouts; int64_t param2; uint8_t funcid; CTransaction vinTx; uint256 hashBlock; int64_t inputs=0,outputs=0; - if ((numvouts=tx.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3, version, confirmation))!=0) + if ((numvouts=tx.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3))!=0) { switch (funcid) { case 'O': return (true); - case 'P': case 'C': case 'R': - if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 ) + case 'P': + if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 ) + return eval->Invalid("cant find vinTx"); + inputs = vinTx.vout[tx.vin[1].prevout.n].nValue; + outputs = tx.vout[0].nValue + tx.vout[3].nValue; + break; + case 'C': + if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 ) return eval->Invalid("cant find vinTx"); - inputs = vinTx.vout[tx.vin[0].prevout.n].nValue; - outputs = tx.vout[0].nValue; - if (funcid=='P') outputs+=tx.vout[3].nValue; - break; + inputs = vinTx.vout[tx.vin[1].prevout.n].nValue; + outputs = tx.vout[0].nValue; + break; + case 'R': + if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 ) + return eval->Invalid("cant find vinTx"); + inputs = vinTx.vout[tx.vin[1].prevout.n].nValue; + outputs = tx.vout[2].nValue; + break; default: return (false); } if ( inputs != outputs ) { - LOGSTREAM("channelscc",CCLOG_ERROR, stream << "inputs " << inputs << " vs outputs " << outputs << std::endl); + LOGSTREAM("channelscc",CCLOG_INFO, stream << "inputs " << inputs << " vs outputs " << outputs << std::endl); return eval->Invalid("mismatched inputs != outputs"); } else return (true); @@ -173,322 +181,213 @@ bool ChannelsExactAmounts(Eval* eval,const CTransaction &tx) return(false); } -bool ValidateChannelOpenTx(Eval* eval,const CTransaction& channelOpenTx, uint256 tokenid, char* channeladdress,char* srcmarker,char* destmarker,char* srctokensaddr,char* srcaddr,int32_t numpayments, int64_t payment) -{ - int32_t i=0,numvouts; uint8_t funcid; struct CCcontract_info *cp,C; CTransaction prevTx; uint256 hashblock,tmptokenid; - std::vector keys; std::vector oprets; - - CCOpretCheck(eval,channelOpenTx,true,true,true); - ExactAmounts(eval,channelOpenTx,ASSETCHAINS_CCZEROTXFEE[EVAL_CHANNELS]?0:CC_TXFEE); - if (tokenid==zeroid) - { - for (int i=0;iInvalid("vin."+std::to_string(i)+" is normal for channelopen!"); - if (channelOpenTx.vout.size()!=5) - return eval->Invalid("invalid number of vouts for channelsopen tx!"); - else if (ConstrainVout(channelOpenTx.vout[3],0,srcaddr,0)==0 ) - return eval->Invalid("vout.3 is normal change for channelopen!"); - } - else - { - i=0; - cp = CCinit(&C,EVAL_TOKENS); - while (iismyvin)(channelOpenTx.vin[i].scriptSig) != 0) - { - if (myGetTransaction(channelOpenTx.vin[i].prevout.hash,prevTx,hashblock)!= 0 && (numvouts=prevTx.vout.size()) > 0 - && (funcid=DecodeTokenOpRetV1(prevTx.vout[numvouts-1].scriptPubKey, tmptokenid, keys, oprets))!=0 - && ((funcid=='c' && prevTx.GetHash()==tokenid) || (funcid!='c' && tmptokenid==tokenid))) - i++; - else break; - } - while (iInvalid("invalid vin structure for channelopen!"); - else if (channelOpenTx.vout.size()!=6) - return eval->Invalid("invalid number of vouts for channelsopen tx!"); - else if (ConstrainVout(channelOpenTx.vout[3],1,srctokensaddr,0)==0 ) - return eval->Invalid("vout.3 is Tokens CC change to srcpub for channelopen or invalid amount!"); - else if (ConstrainVout(channelOpenTx.vout[4],0,srcaddr,0)==0 ) - return eval->Invalid("vout.4 is normal change for channelopen!"); - } - if (numpayments<1 || numpayments>CHANNELS_MAXPAYMENTS) - return eval->Invalid("invalid number of payments for channelsopen tx!"); - else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) - return eval->Invalid("vout.0 is CC for channelopen or invalid amount!"); - else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) - return eval->Invalid("vout.1 is CC marker to srcpub for channelopen or invalid amount !"); - else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) - return eval->Invalid("vout.2 is CC marker to destpub for channelopen or invalid amount!"); - return (true); -} - -bool ValidateChannelCloseTx(Eval* eval, uint256 closetxid, uint256 opentxid) -{ - CTransaction tx; uint256 hashblock,tokenid,param3,tmptxid; int32_t numvouts,param1; int64_t param2; CPubKey srcpub,destpub; - uint8_t version; uint16_t tmp; - - if (myGetTransaction(closetxid,tx,hashblock)==0 || (numvouts=tx.vout.size())<1) - return eval->Invalid("invalid channelsclose tx"); - else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, tmptxid, srcpub, destpub, param1, param2, param3, version, tmp)!='C') - return eval->Invalid("invalid channelsclose OP_RETURN data"); - else if (tmptxid!=opentxid) - return eval->Invalid("invalid channelsclose not matching this channelsopen txid"); - return (true); -} - -bool ValidateNormalVins(Eval* eval, const CTransaction& tx,int32_t index) -{ - for (int i=index;iInvalid("vin."+std::to_string(i)+" is normal for channel tx!"); - return (true); -} - -bool ValidateChannelVin(struct CCcontract_info *cp,Eval* eval, const CTransaction& tx,int32_t index, uint256 opentxid, char* fromaddr,int64_t amount) -{ - CTransaction prevTx; uint256 hashblock,tokenid,tmp_txid,p3; CPubKey srcpub,destpub; int32_t p1,numvouts; - int64_t p2; uint8_t version; uint16_t tmp; char tmpaddr[64]; - - if ((*cp->ismyvin)(tx.vin[index].scriptSig) == 0) - return eval->Invalid("vin."+std::to_string(index)+" is channel CC for channel tx!"); - else if (myGetTransaction(tx.vin[index].prevout.hash,prevTx,hashblock) == 0) - return eval->Invalid("vin."+std::to_string(index)+" tx does not exist!"); - else if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3, version, tmp) == 0) - return eval->Invalid("invalid vin."+std::to_string(index)+" tx OP_RETURN data!"); - else if (fromaddr!=0 && Getscriptaddress(tmpaddr,prevTx.vout[tx.vin[index].prevout.n].scriptPubKey) && strcmp(tmpaddr,fromaddr)!=0) - return eval->Invalid("invalid vin."+std::to_string(index)+" address!"); - else if (amount>0 && prevTx.vout[tx.vin[index].prevout.n].nValue!=amount) - return eval->Invalid("vin."+std::to_string(index)+" invalid amount!"); - else if (prevTx.GetHash()!=opentxid && opentxid!=tmp_txid) - return eval->Invalid("invalid vin."+std::to_string(index)+" tx, different opent txid!"); - return (true); -} - bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - int32_t numvins,numvouts,i,numpayments,p1,param1; uint16_t confirmation,tmp; - uint256 txid,hashblock,p3,param3,opentxid,tmp_txid,genhashchain,hashchain,tokenid,tmptokenid; - uint8_t funcid,hash[32],hashdest[32],version; char channeladdress[65],srcmarker[65],destmarker[65],destaddr[65],srcaddr[65],desttokensaddr[65],srctokensaddr[65]; + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numpayments,p1,param1; bool retval; + uint256 txid,hashblock,p3,param3,opentxid,tmp_txid,genhashchain,hashchain,tokenid; + uint8_t funcid,hash[32],hashdest[32]; char channeladdress[65],srcmarker[65],destmarker[65],destaddr[65],srcaddr[65],desttokensaddr[65],srctokensaddr[65]; int64_t p2,param2,payment; CPubKey srcpub, destpub; CTransaction channelOpenTx,channelCloseTx,prevTx; - if (strcmp(ASSETCHAINS_SYMBOL, "MORTY") == 0 && GetLatestTimestamp(eval->GetCurrentHeight())Invalid("no vouts"); else { - CCOpretCheck(eval,tx,true,true,true); - ExactAmounts(eval,tx,ASSETCHAINS_CCZEROTXFEE[EVAL_CHANNELS]?0:CC_TXFEE); - if (ChannelsExactAmounts(eval,tx) == false ) + if (ChannelsExactAmounts(cp,eval,tx,1,10000) == false ) { - return (false); - } - if ( (funcid = DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, opentxid, srcpub, destpub, param1, param2, param3, version, tmp)) != 0) + return eval->Invalid("invalid channel inputs vs. outputs!"); + } + else { - if (myGetTransaction(opentxid,channelOpenTx,hashblock)== 0) - return eval->Invalid("invalid channelopen tx!"); - else if ((channelOpenTx.vout.size()) > 0 && (DecodeChannelsOpRet(channelOpenTx.vout[channelOpenTx.vout.size()-1].scriptPubKey, tmptokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain, version, confirmation)) != 'O') - return eval->Invalid("invalid channelopen OP_RETURN data!"); - else if (tmptokenid!=tokenid) - return eval->Invalid("invalid different tokenid in channelsopen and current tx!"); - if (tokenid==zeroid) GetCCaddress1of2(cp,channeladdress,srcpub,destpub); - else GetTokensCCaddress1of2(cp,channeladdress,srcpub,destpub); - GetCCaddress(cp,srcmarker,srcpub); - GetCCaddress(cp,destmarker,destpub); - Getscriptaddress(srcaddr,CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG); - Getscriptaddress(destaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG); - _GetCCaddress(srctokensaddr,EVAL_TOKENS,srcpub); - _GetCCaddress(desttokensaddr,EVAL_TOKENS,destpub); - switch ( funcid ) + txid = tx.GetHash(); + memcpy(hash,&txid,sizeof(hash)); + if ( (funcid = DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, opentxid, srcpub, destpub, param1, param2, param3)) != 0) { - case 'O': - //normal coins - //vin.0: normal input - //... - //vin.n-1: normal input - //tokens - //vin.0: Tokens input - //... - //vin.m: Tokens input - //vin.n-m+1: normal input - //... - //vin.n-1: normal input - //vout.0: CC vout for channel funding on CC1of2 pubkey - //vout.1: CC vout marker to senders pubkey - //vout.2: CC vout marker to receiver pubkey - //normal coins - //vout.3: normal change - //vout.4: opreturn - 'O' zerotxid senderspubkey receiverspubkey totalnumberofpayments paymentamount hashchain version confirmation - //tokens - //vout.3: tokens CC change - //vout.4: normal change - //vout.5: opreturn - 'O' zerotxid senderspubkey receiverspubkey totalnumberofpayments paymentamount hashchain version confirmation - return eval->Invalid("unexpected ChannelsValidate for channelsopen!"); - case 'P': - //vin.0: CC input from channel funding - //vin.1: CC input from src marker - //vin.2: normal input - //... - //vin.n-1: normal input - //vout.0: CC vout change to CC1of2 pubkey - //vout.1: CC vout marker to senders pubkey - //vout.2: CC vout marker to receiver pubkey - //vout.3: normal/tokens CC output of payment amount to receiver pubkey - //vout.4: normal change - //vout.5: opreturn - 'P' opentxid senderspubkey receiverspubkey depth numpayments secret version confirmation - if (numvouts!=6) - return eval->Invalid("invalid number of vouts for channelspayment tx!"); - else if (ValidateChannelOpenTx(eval,channelOpenTx,tokenid,channeladdress,srcmarker,destmarker,srctokensaddr,srcaddr,numpayments,payment)==0) - return (false); - else if (confirmation>0 && komodo_txnotarizedconfirmed(channelOpenTx.GetHash(),confirmation) == 0) - return eval->Invalid("channelopen is not yet confirmed(notarised)!"); - else if ( ValidateChannelVin(cp,eval,tx,0,opentxid,channeladdress,0) == 0 ) - return (false); - else if ( ValidateChannelVin(cp,eval,tx,1,opentxid,0,CC_MARKER_VALUE) == 0 ) - return (false); - else if ( ValidateNormalVins(eval,tx,2) == 0 ) - return (false); - else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) - return eval->Invalid("vout.1 is CC marker for channelpayment to srcpub or invalid amount!"); - else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) - return eval->Invalid("vout.2 is CC marker for channelpayment to destpub or invalid amount!"); - else if ( tokenid!=zeroid && ConstrainVout(tx.vout[3],1,desttokensaddr,param2*payment)==0 ) - return eval->Invalid("vout.3 is Tokens CC for channelpayment or invalid amount or invalid receiver!"); - else if ( tokenid==zeroid && ConstrainVout(tx.vout[3],0,destaddr,param2*payment)==0 ) - return eval->Invalid("vout.3 is normal for channelpayment or invalid amount or invalid receiver!"); - else if ( ConstrainVout(tx.vout[4],0,0,0)==0 ) - return eval->Invalid("vout.4 is normal change!"); - else if ( param1 > numpayments || param2 > numpayments) - return eval->Invalid("too many payment increments!"); - else if ( param2 < 1) - return eval->Invalid("invalid depth or number of payments!"); - else - { - endiancpy(hash, (uint8_t * ) & param3, 32); - for (i = 0; i < numpayments-param1; i++) + if (myGetTransaction(opentxid,channelOpenTx,hashblock)== 0) + return eval->Invalid("invalid channelopen tx!"); + else if ((numvouts=channelOpenTx.vout.size()) > 0 && (DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain)) != 'O') + return eval->Invalid("invalid channelopen OP_RETURN data!"); + GetCCaddress1of2(cp,channeladdress,srcpub,destpub); + GetCCaddress(cp,srcmarker,srcpub); + GetCCaddress(cp,destmarker,destpub); + Getscriptaddress(srcaddr,CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG); + Getscriptaddress(destaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG); + _GetCCaddress(srctokensaddr,EVAL_TOKENS,srcpub); + _GetCCaddress(desttokensaddr,EVAL_TOKENS,destpub); + switch ( funcid ) + { + case 'O': + //vin.0: normal input + //vout.0: CC vout for channel funding on CC1of2 pubkey + //vout.1: CC vout marker to senders pubKey + //vout.2: CC vout marker to receiver pubkey + //vout.n-2: normal change + //vout.n-1: opreturn - 'O' zerotxid senderspubkey receiverspubkey totalnumberofpayments paymentamount hashchain + return eval->Invalid("unexpected ChannelsValidate for channelsopen!"); + case 'P': + //vin.0: normal input + //vin.1: CC input from channel funding + //vin.2: CC input from src marker + //vout.0: CC vout change to CC1of2 pubkey + //vout.1: CC vout marker to senders pubKey + //vout.2: CC vout marker to receiver pubkey + //vout.3: normal output of payment amount to receiver pubkey + //vout.n-2: normal change + //vout.n-1: opreturn - 'P' opentxid senderspubkey receiverspubkey depth numpayments secret + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); + else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelpayment!"); + else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 ) + return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" is CC for channelpayment!"); + else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelpayment!"); + else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelpayment!"); + else if ( tokenid!=zeroid && ConstrainVout(tx.vout[3],1,desttokensaddr,param2*payment)==0 ) + return eval->Invalid("vout.3 is CC or invalid amount or invalid receiver for channelpayment!"); + else if ( tokenid==zeroid && ConstrainVout(tx.vout[3],0,destaddr,param2*payment)==0 ) + return eval->Invalid("vout.3 is normal or invalid amount or invalid receiver for channelpayment!"); + else if ( param1 > CHANNELS_MAXPAYMENTS) + return eval->Invalid("too many payment increments!"); + else + { + endiancpy(hash, (uint8_t * ) & param3, 32); + for (i = 0; i < numpayments-param1; i++) + { + vcalc_sha256(0, hashdest, hash, 32); + memcpy(hash, hashdest, 32); + } + endiancpy((uint8_t*)&genhashchain,hashdest,32); + if (hashchain!=genhashchain) + return eval->Invalid("invalid secret for payment, does not reach final hashchain!"); + else if (tx.vout[3].nValue != param2*payment) + return eval->Invalid("vout amount does not match number_of_payments*payment!"); + if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) + { + if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) + return eval->Invalid("invalid previous tx OP_RETURN data!"); + else if ( ConstrainVout(tx.vout[0],1,channeladdress,(p1-param2)*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid CC change amount for channelpayment!"); + else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelpayment!"); + else if (param1+param2!=p1) + return eval->Invalid("invalid payment depth!"); + else if (tx.vout[3].nValue > prevTx.vout[0].nValue) + return eval->Invalid("not enough funds in channel for that amount!"); + } + } + break; + case 'C': + //vin.0: normal input + //vin.1: CC input from channel funding + //vin.2: CC input from src marker + //vout.0: CC vout for channel funding + //vout.1: CC vout marker to senders pubKey + //vout.2: CC vout marker to receiver pubkey + //vout.n-2: normal change + //vout.n-1: opreturn - 'C' opentxid senderspubkey receiverspubkey numpayments payment 0 + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); + else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelclose!"); + else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 ) + return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" is CC for channelclose!"); + else if ( ConstrainVout(tx.vout[0],1,channeladdress,0)==0 ) + return eval->Invalid("vout.0 is CC for channelclose!"); + else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelclose!"); + else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelclose!"); + else if ( param1 > CHANNELS_MAXPAYMENTS) + return eval->Invalid("too many payment increments!"); + else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) { - vcalc_sha256(0, hashdest, hash, 32); - memcpy(hash, hashdest, 32); + if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) + return eval->Invalid("invalid previous tx OP_RETURN data!"); + else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelclose!"); + else if (tx.vout[0].nValue != prevTx.vout[0].nValue) + return eval->Invalid("invalid CC amount, amount must match funds in channel"); } - endiancpy((uint8_t*)&genhashchain,hashdest,32); - if (hashchain!=genhashchain) - return eval->Invalid("invalid secret for payment, does not reach final hashchain!"); - if (myGetTransaction(tx.vin[0].prevout.hash,prevTx,hashblock) != 0) + break; + case 'R': + //vin.0: normal input + //vin.1: CC input from channel funding + //vin.2: CC input from src marker + //vout.0: CC vout marker to senders pubKey + //vout.1: CC vout marker to receiver pubKey + //vout.2: normal output of CC input to senders pubkey + //vout.n-2: normal change + //vout.n-1: opreturn - 'R' opentxid senderspubkey receiverspubkey numpayments payment closetxid + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); + else if (komodo_txnotarizedconfirmed(param3) == 0) + return eval->Invalid("channelClose is not yet confirmed(notarised)!"); + else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelrefund!"); + else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 ) + return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" CC for channelrefund!"); + else if ( ConstrainVout(tx.vout[0],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.0 is CC marker to srcpub or invalid amount for channelrefund!"); + else if ( ConstrainVout(tx.vout[1],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to destpub or invalid amount for channelrefund!"); + else if ( tokenid!=zeroid && ConstrainVout(tx.vout[2],1,srctokensaddr,param1*payment)==0 ) + return eval->Invalid("vout.2 is CC or invalid amount or invalid receiver for channelrefund!"); + else if ( tokenid==zeroid && ConstrainVout(tx.vout[2],0,srcaddr,param1*payment)==0 ) + return eval->Invalid("vout.2 is normal or invalid amount or invalid receiver for channelrefund!"); + else if ( param1 > CHANNELS_MAXPAYMENTS) + return eval->Invalid("too many payment increments!"); + else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) { - if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3, version, tmp) == 0) + if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if ( ConstrainVout(tx.vout[0],1,channeladdress,(p1-param2)*payment)==0 ) - return eval->Invalid("vout.0 is CC for channelpayment or invalid CC change amount!"); - else if (param1+param2!=p1) - return eval->Invalid("invalid payment depth!"); - else if (tx.vout[3].nValue > prevTx.vout[0].nValue) - return eval->Invalid("not enough funds in channel for that amount!"); + else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelrefund!"); + else if (tx.vout[2].nValue != prevTx.vout[0].nValue) + return eval->Invalid("invalid amount, refund amount and funds in channel must match!"); } - } - break; - case 'C': - //vin.0: CC input from channel funding - //vin.1: CC input from src marker - //vin.2: normal input - //... - //vin.n-1: normal input - //vout.0: CC vout for channel funding - //vout.1: CC vout marker to senders pubkey - //vout.2: CC vout marker to receiver pubkey - //vout.3: normal change - //vout.4: opreturn - 'C' opentxid senderspubkey receiverspubkey numpayments payment 0 version confirmation - if (numvouts!=5) - return eval->Invalid("invalid number of vouts for channelsclose tx!"); - else if (ValidateChannelOpenTx(eval,channelOpenTx,tokenid,channeladdress,srcmarker,destmarker,srctokensaddr,srcaddr,numpayments,payment)==0) - return (false); - else if ( ValidateChannelVin(cp,eval,tx,0,opentxid,channeladdress,param1*payment) == 0 ) - return (false); - else if ( ValidateChannelVin(cp,eval,tx,1,opentxid,srcmarker,CC_MARKER_VALUE) == 0 ) - return (false); - else if ( ValidateNormalVins(eval,tx,2) == 0 ) - return (false); - else if ( ConstrainVout(tx.vout[0],1,channeladdress,param1*payment)==0 ) - return eval->Invalid("vout.0 is CC for channelclose or invalid amount!"); - else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) - return eval->Invalid("vout.1 is CC marker for channelclose to srcpub or invalid amount!"); - else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) - return eval->Invalid("vout.2 is CC marker for channelclose to destpub or invalid amount!"); - else if ( ConstrainVout(tx.vout[3],0,srcaddr,0)==0 ) - return eval->Invalid("vout.3 is normal change!"); - else if ( param1 > numpayments) - return eval->Invalid("too many payment increments!"); - else if ( param2 > payment) - return eval->Invalid("invalid payment size in OP_RETURN data for channelsclose tx!"); - else if (myGetTransaction(tx.vin[0].prevout.hash,prevTx,hashblock) != 0) - { - if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3, version, tmp) == 0) - return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if (tx.vout[0].nValue != prevTx.vout[0].nValue) - return eval->Invalid("invalid CC amount, amount must match funds in channel"); - } - break; - case 'R': - //vin.0: CC input from channel funding - //vin.1: CC input from src marker - //vin.2: normal input - //... - //vin.n-1: normal input - //vout.0: normal/tokens CC output to senders pubkey - //vout.1: CC vout marker to senders pubkey - //vout.2: CC vout marker to receiver pubKey - //vout.3: normal change - //vout.4: opreturn - 'R' opentxid senderspubkey receiverspubkey numpayments payment closetxid version confirmation - if (numvouts!=5) - return eval->Invalid("invalid number of vouts for channelsrefund tx!"); - else if (ValidateChannelOpenTx(eval,channelOpenTx,tokenid,channeladdress,srcmarker,destmarker,srctokensaddr,srcaddr,numpayments,payment)==0) - return (false); - else if (ValidateChannelCloseTx(eval,param3,opentxid)==0) - return (false); - else if (komodo_txnotarizedconfirmed(opentxid,confirmation) == 0) - return eval->Invalid("channelopen is not yet confirmed(notarised)!"); - else if (komodo_txnotarizedconfirmed(param3,confirmation) == 0) - return eval->Invalid("channelClose is not yet confirmed(notarised)!"); - else if ( ValidateChannelVin(cp,eval,tx,0,opentxid,channeladdress,param1*param2) == 0 ) - return (false); - else if ( ValidateChannelVin(cp,eval,tx,1,opentxid,srcmarker,CC_MARKER_VALUE) == 0 ) - return (false); - else if ( ValidateNormalVins(eval,tx,2) == 0 ) - return (false); - else if ( tokenid!=zeroid && ConstrainVout(tx.vout[0],1,srctokensaddr,param1*payment)==0 ) - return eval->Invalid("vout.0 is Tokens CC for channelrefund or invalid amount or invalid receiver!"); - else if ( tokenid==zeroid && ConstrainVout(tx.vout[0],0,srcaddr,param1*payment)==0 ) - return eval->Invalid("vout.0 is normal for channelrefund or invalid amount or invalid receiver!"); - else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) - return eval->Invalid("vout.1 is CC marker for channelrefund to srcpub or invalid amount!"); - else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) - return eval->Invalid("vout.2 is CC marker for channelrefund to destpub or invalid amount!"); - else if ( ConstrainVout(tx.vout[3],0,srcaddr,0)==0 ) - return eval->Invalid("vout.3 is normal change!"); - else if ( param1 > numpayments) - return eval->Invalid("too many payment increments!"); - else if ( param2 > payment) - return eval->Invalid("invalid payment size in OP_RETURN data for channelsrefund tx!"); - else if (myGetTransaction(tx.vin[0].prevout.hash,prevTx,hashblock) != 0) - { - if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3, version, tmp) == 0) - return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if (tx.vout[0].nValue != prevTx.vout[0].nValue) - return eval->Invalid("invalid amount, refund amount and funds in channel must match!"); - } - break; - default: - fprintf(stderr,"illegal channels funcid.(%c)\n",funcid); - return eval->Invalid("unexpected channels funcid"); + break; + default: + fprintf(stderr,"illegal channels funcid.(%c)\n",funcid); + return eval->Invalid("unexpected channels funcid"); + } } + else return eval->Invalid("unexpected channels missing funcid"); + retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); + if ( retval != 0 ) + LOGSTREAM("channels",CCLOG_INFO, stream << "Channels tx validated" << std::endl); + else fprintf(stderr,"Channels tx invalid\n"); + return(retval); } - else return eval->Invalid("invalid opret vout for channels tx"); - LOGSTREAM("channels",CCLOG_INFO, stream << "Channels tx validated" << std::endl); - return(true); } } // end of consensus code @@ -497,10 +396,12 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, CTransaction openTx, uint256 &prevtxid, CPubKey mypk) { - char coinaddr[65],funcid; int64_t param2,totalinputs = 0,numvouts; uint256 txid=zeroid,tmp_txid,hashBlock,param3,tokenid; CTransaction tx; int32_t marker,param1; - std::vector > unspentOutputs; CPubKey srcpub,destpub; uint8_t myprivkey[32],version; uint16_t confirmation; + char coinaddr[65]; int64_t param2,totalinputs = 0,numvouts; uint256 txid=zeroid,tmp_txid,hashBlock,param3,tokenid; CTransaction tx; int32_t marker,param1; + std::vector > unspentOutputs; + CPubKey srcpub,destpub; + uint8_t myprivkey[32]; - if ((numvouts=openTx.vout.size()) > 0 && DecodeChannelsOpRet(openTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3,version,confirmation)=='O') + if ((numvouts=openTx.vout.size()) > 0 && DecodeChannelsOpRet(openTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)=='O') { if (tokenid!=zeroid) GetTokensCCaddress1of2(cp,coinaddr,srcpub,destpub); else GetCCaddress1of2(cp,coinaddr,srcpub,destpub); @@ -517,7 +418,7 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C { if ( (int32_t)it->first.index==0 && myGetTransaction(it->first.txhash,tx,hashBlock) != 0 && (numvouts=tx.vout.size()) > 0) { - if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3,version,confirmation)!=0 && + if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 && (tmp_txid==openTx.GetHash() || tx.GetHash()==openTx.GetHash()) && IsChannelsMarkervout(cp,tx,marker==1?srcpub:destpub,marker)>0 && (totalinputs=IsChannelsvout(cp,tx,srcpub,destpub,0))>0) { @@ -531,13 +432,13 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C txid=zeroid; int32_t mindepth=CHANNELS_MAXPAYMENTS; std::vector tmp_txs; - myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,0); + myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,'P'); for (std::vector::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++) { const CTransaction &txmempool = *it; const uint256 &hash = txmempool.GetHash(); - if ((numvouts=txmempool.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3,version,confirmation))!=0 && (funcid=='P' || funcid=='C') && + if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)=='P' && tmp_txid==openTx.GetHash() && param1 < mindepth) { txid=hash; @@ -560,7 +461,7 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C else return 0; } -UniValue ChannelOpen(const CPubKey& pk, uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment,uint16_t confirmation,uint256 tokenid) +UniValue ChannelOpen(const CPubKey& pk, uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment, uint256 tokenid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); uint8_t hash[32],hashdest[32]; uint64_t amount,tokens=0,funds; int32_t i; uint256 hashchain,entropy,hentropy; @@ -577,13 +478,13 @@ UniValue ChannelOpen(const CPubKey& pk, uint64_t txfee,CPubKey destpub,int32_t n cp = CCinit(&C,EVAL_CHANNELS); cpTokens = CCinit(&CTokens,EVAL_TOKENS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_CHANNELS]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); funds = numpayments * payment; if (tokenid!=zeroid) { - tokens=AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, funds, 64); amount=AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,5,pk.IsValid()); + tokens=AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, funds, 64); } else amount=AddNormalinputs(mtx,mypk,funds+txfee+2*CC_MARKER_VALUE,64,pk.IsValid()); if (amount+tokens >= funds+txfee+2*CC_MARKER_VALUE) @@ -600,8 +501,8 @@ UniValue ChannelOpen(const CPubKey& pk, uint64_t txfee,CPubKey destpub,int32_t n else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub)); mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); - if (tokenid!=zeroid && tokens>=funds) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokens-funds,mypk)); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('O',tokenid,zeroid,mypk,destpub,numpayments,payment,hashchain,CHANNELCC_VERSION,confirmation))); + if (tokenid!=zeroid && tokens>funds) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokens-funds,mypk)); + return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('O',tokenid,zeroid,mypk,destpub,numpayments,payment,hashchain))); } CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding funds"); } @@ -610,20 +511,20 @@ UniValue ChannelPayment(const CPubKey& pk, uint64_t txfee,uint256 opentxid,int64 { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CPubKey mypk,srcpub,destpub; uint256 txid,hashchain,gensecret,hashblock,entropy,hentropy,prevtxid,param3,tokenid; - struct CCcontract_info *cp,C; int32_t i,funcid,prevdepth,numvouts,numpayments,totalnumpayments; uint16_t confirmation,tmpc; + struct CCcontract_info *cp,C; int32_t i,funcid,prevdepth,numvouts,numpayments,totalnumpayments; int64_t payment,change,funds,param2; - uint8_t hash[32],hashdest[32],version,tmpv; + uint8_t hash[32],hashdest[32]; CTransaction channelOpenTx,prevTx; cp = CCinit(&C,EVAL_CHANNELS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_CHANNELS]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); if (amount <1) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid payment amount, must be greater than 0"); if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid"); - if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, totalnumpayments, payment, hashchain, version, confirmation)=='O') + if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, totalnumpayments, payment, hashchain)=='O') { if (mypk != srcpub && mypk != destpub) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "this is not our channel"); @@ -633,16 +534,15 @@ UniValue ChannelPayment(const CPubKey& pk, uint64_t txfee,uint256 opentxid,int64 } else CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open tx"); - if (confirmation>0 && komodo_txnotarizedconfirmed(opentxid,confirmation)==false) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsopen tx not yet confirmed/notarized"); - if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && (change=funds-amount)>=0) + if (komodo_txnotarizedconfirmed(opentxid)==false) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsopen tx not yet confirmed/notarized"); + if (AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0) { - if (AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) >= txfee+CC_MARKER_VALUE) + if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && (change=funds-amount)>=0) { numpayments=amount/payment; if (myGetTransaction(prevtxid,prevTx,hashblock) != 0 && (numvouts=prevTx.vout.size()) > 0 && - ((funcid = DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, prevdepth, param2, param3, tmpv, tmpc)) != 0) && - (funcid == 'O' || funcid=='P' || funcid=='C')) + ((funcid = DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, prevdepth, param2, param3)) != 0) && + (funcid == 'P' || funcid=='O')) { if (numpayments > prevdepth) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "not enough funds in channel for that amount"); @@ -683,52 +583,57 @@ UniValue ChannelPayment(const CPubKey& pk, uint64_t txfee,uint256 opentxid,int64 mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, amount, destpub)); else mtx.vout.push_back(CTxOut(amount, CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG)); - return (FinalizeCCTxExt(pk.IsValid(), 0, cp, mtx, mypk, txfee, EncodeChannelsOpRet('P', tokenid, opentxid, srcpub, destpub, prevdepth-numpayments, numpayments, secret, CHANNELCC_VERSION, confirmation))); + return (FinalizeCCTxExt(pk.IsValid(), 0, cp, mtx, mypk, txfee, EncodeChannelsOpRet('P', tokenid, opentxid, srcpub, destpub, prevdepth-numpayments, numpayments, secret))); } - else CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs"); + else + CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs"); } - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs"); + CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs"); } UniValue ChannelClose(const CPubKey& pk, uint64_t txfee,uint256 opentxid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CPubKey mypk,srcpub,destpub; struct CCcontract_info *cp,C; - CTransaction channelOpenTx; uint256 hashblock,tmp_txid,prevtxid,hashchain,tokenid; int32_t numvouts,numpayments; - int64_t payment,funds; uint16_t confirmation; uint8_t version; + CTransaction channelOpenTx; + uint256 hashblock,tmp_txid,prevtxid,hashchain,tokenid; + int32_t numvouts,numpayments; + int64_t payment,funds; // verify this is one of our outbound channels cp = CCinit(&C,EVAL_CHANNELS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_CHANNELS]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid"); - if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain,version,confirmation)!='O') + if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain)!='O') CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open tx"); + if (komodo_txnotarizedconfirmed(opentxid)==false) + CCERR_RESULT("channelscc",CCLOG_INFO, stream <<"channelsopen tx not yet confirmed/notarized"); if (mypk != srcpub) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "cannot close, you are not channel owner"); - if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0 ) { - if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) >= txfee+CC_MARKER_VALUE ) + if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) { if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub)); else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub)); mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',tokenid,opentxid,mypk,destpub,funds/payment,payment,zeroid,CHANNELCC_VERSION,confirmation))); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',tokenid,opentxid,mypk,destpub,funds/payment,payment,zeroid))); } else - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs"); + CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs"); } - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs"); + CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs"); } UniValue ChannelRefund(const CPubKey& pk, uint64_t txfee,uint256 opentxid,uint256 closetxid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CPubKey mypk; struct CCcontract_info *cp,C; int64_t funds,payment,param2; - int32_t i,numpayments,numvouts,param1; uint16_t confirmation,tmpc; uint8_t version,tmpv; + int32_t i,numpayments,numvouts,param1; uint256 hashchain,hashblock,txid,prevtxid,param3,tokenid; CTransaction channelOpenTx,channelCloseTx,prevTx; CPubKey srcpub,destpub; @@ -736,111 +641,56 @@ UniValue ChannelRefund(const CPubKey& pk, uint64_t txfee,uint256 opentxid,uint25 // verify stoptxid and origtxid match and are mine cp = CCinit(&C,EVAL_CHANNELS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_CHANNELS]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid"); - if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,numpayments,payment,hashchain,version,confirmation)!='O') - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open tx"); - if (komodo_txnotarizedconfirmed(opentxid,confirmation)==false) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsopen tx not yet confirmed/notarized"); if (myGetTransaction(closetxid,channelCloseTx,hashblock) == 0) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel close txid"); - if ((numvouts=channelCloseTx.vout.size()) < 1 || DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,param1,param2,param3,tmpv,tmpc)!='C') + if ((numvouts=channelCloseTx.vout.size()) < 1 || DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,param1,param2,param3)!='C') CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel close tx"); - if (komodo_txnotarizedconfirmed(closetxid,confirmation)==false) + if (komodo_txnotarizedconfirmed(closetxid)==false) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsclose tx not yet confirmed/notarized"); if (txid!=opentxid) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "open and close txid are not from same channel"); + if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) + CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid"); + if (komodo_txnotarizedconfirmed(opentxid)==false) + CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsopen tx not yet confirmed/notarized"); + if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,numpayments,payment,hashchain)!='O') + CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open tx"); if (mypk != srcpub) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "cannot refund, you are not the channel owner"); - if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0 ) { - if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) >= txfee+CC_MARKER_VALUE ) + if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) { if ((myGetTransaction(prevtxid,prevTx,hashblock) != 0) && (numvouts=prevTx.vout.size()) > 0 && - DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3,tmpv,tmpc) != 0) + DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3) != 0) { - if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,funds,mypk)); - else mtx.vout.push_back(CTxOut(funds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',tokenid,opentxid,mypk,destpub,funds/payment,payment,closetxid,CHANNELCC_VERSION,confirmation))); + if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,funds,mypk)); + else mtx.vout.push_back(CTxOut(funds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',tokenid,opentxid,mypk,destpub,funds/payment,payment,closetxid))); } else CCERR_RESULT("channelscc",CCLOG_INFO, stream << "previous tx is invalid"); } else - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs"); - } - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs"); -} - -UniValue ChannelGenerateSecret(const CPubKey& pk,uint256 opentxid,int64_t amount) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,srcpub,destpub; uint256 txid,hashchain,gensecret,hashblock,entropy,hentropy,prevtxid,param3,tokenid; - struct CCcontract_info *cp,C; int32_t i,funcid,prevdepth,numvouts,numpayments,totalnumpayments; uint16_t confirmation,tmpc; - int64_t payment,change,funds,param2; UniValue result(UniValue::VOBJ); - uint8_t hash[32],hashdest[32],version,tmpv; - CTransaction channelOpenTx,prevTx; - - result.push_back(Pair("result","success")); - cp = CCinit(&C,EVAL_CHANNELS); - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - if (amount <1) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid payment amount, must be greater than 0"); - if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid"); - if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, totalnumpayments, payment, hashchain, version, confirmation)=='O') - { - if (mypk != srcpub) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "this is not our channel"); - else if (amount % payment != 0 || amount=0) - { - if (myGetTransaction(prevtxid,prevTx,hashblock) != 0 && (numvouts=prevTx.vout.size()) > 0 && - ((funcid = DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, prevdepth, param2, param3, tmpv, tmpc)) != 0) && - (funcid == 'O' || funcid=='P' || funcid=='C')) - { - if (numpayments > prevdepth) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "not enough funds in channel for that amount"); - else if (numpayments == 0) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid amount"); - hentropy = DiceHashEntropy(entropy,channelOpenTx.vin[0].prevout.hash,channelOpenTx.vin[0].prevout.n,1); - if (prevdepth-numpayments) - { - endiancpy(hash, (uint8_t * ) & hentropy, 32); - for (i = 0; i < prevdepth-numpayments; i++) - { - vcalc_sha256(0, hashdest, hash, 32); - memcpy(hash, hashdest, 32); - } - endiancpy((uint8_t * ) & gensecret, hashdest, 32); - } - else endiancpy((uint8_t * ) & gensecret, (uint8_t * ) & hentropy, 32); - result.push_back(Pair("payment secret",gensecret.GetHex())); - return (result); - } - else CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid previous channel tx"); + CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs"); } - else CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs"); + CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs"); } UniValue ChannelsList(const CPubKey& pk) { UniValue result(UniValue::VOBJ); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,tmp_txid,param3,tokenid; CTransaction tx; char myCCaddr[65],str[512],pub[34]; CPubKey mypk,srcpub,destpub; int32_t vout,numvouts,param1; - int64_t nValue,param2; uint16_t confirmation; uint8_t version; + int64_t nValue,param2; cp = CCinit(&C,EVAL_CHANNELS); mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); GetCCaddress(cp,myCCaddr,mypk); - SetCCtxids(txids,myCCaddr,true,EVAL_CHANNELS,CC_MARKER_VALUE,zeroid,'O'); + SetCCtxids(txids,myCCaddr,true,EVAL_CHANNELS,zeroid,'O'); result.push_back(Pair("result","success")); result.push_back(Pair("name","Channels List")); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) @@ -848,7 +698,7 @@ UniValue ChannelsList(const CPubKey& pk) txid = *it; if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) { - if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3,version,confirmation) == 'O') + if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O') { sprintf(str,"%lld payments of %lld satoshi to %s",(long long)param1,(long long)param2,pubkey33_str(pub,(uint8_t *)&destpub)); result.push_back(Pair(txid.GetHex().data(),str)); @@ -861,15 +711,15 @@ UniValue ChannelsList(const CPubKey& pk) UniValue ChannelsInfo(const CPubKey& pk,uint256 channeltxid) { UniValue result(UniValue::VOBJ),array(UniValue::VARR); CTransaction tx,opentx; uint256 txid,tmp_txid,hashBlock,param3,opentxid,hashchain,tokenid; - struct CCcontract_info *cp,C; char CCaddr[65],addr[65],str[512],funcid; int32_t vout,numvouts,param1,numpayments; - int64_t param2,payment; CPubKey srcpub,destpub,mypk; uint16_t confirmation,tmpc; uint8_t version,tmpv; + struct CCcontract_info *cp,C; char CCaddr[65],addr[65],str[512]; int32_t vout,numvouts,param1,numpayments; + int64_t param2,payment; CPubKey srcpub,destpub,mypk; std::vector txids; std::vector txs; cp = CCinit(&C,EVAL_CHANNELS); mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); if (myGetTransaction(channeltxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && - (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3,version,confirmation) == 'O')) + (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'O')) { GetCCaddress1of2(cp,CCaddr,srcpub,destpub); Getscriptaddress(addr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG); @@ -889,20 +739,20 @@ UniValue ChannelsInfo(const CPubKey& pk,uint256 channeltxid) result.push_back(Pair("Amount (satoshi)",i64tostr(param1*param2))); } GetCCaddress(cp,CCaddr,mypk); - SetCCtxids(txids,CCaddr,true,EVAL_CHANNELS,CC_MARKER_VALUE,channeltxid,0); + SetCCtxids(txids,CCaddr,true,EVAL_CHANNELS,channeltxid,0); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { if (myGetTransaction(*it,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && - DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3,tmpv,tmpc)!=0 && (tmp_txid==channeltxid || tx.GetHash()==channeltxid)) + DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 && (tmp_txid==channeltxid || tx.GetHash()==channeltxid)) txs.push_back(tx); } std::vector tmp_txs; - myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,0); + myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,'P'); for (std::vector::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++) { const CTransaction &txmempool = *it; - if ((numvouts=txmempool.vout.size()) > 0 && txmempool.vout[1].nValue==CC_MARKER_VALUE && (funcid=DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3,tmpv,tmpc)) != 0 && (funcid=='P' || funcid=='C') && tmp_txid==channeltxid) + if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'P' && tmp_txid==channeltxid) txs.push_back(txmempool); } for (std::vector::const_iterator it=txs.begin(); it!=txs.end(); it++) @@ -911,14 +761,14 @@ UniValue ChannelsInfo(const CPubKey& pk,uint256 channeltxid) if ((numvouts= tx.vout.size()) > 0 ) { UniValue obj(UniValue::VOBJ); - if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3,version,confirmation) == 'O' && tx.GetHash()==channeltxid) + if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O' && tx.GetHash()==channeltxid) { obj.push_back(Pair("Open",tx.GetHash().GetHex().data())); } - else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3,tmpv,tmpc) == 'P' && opentxid==channeltxid) + else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'P' && opentxid==channeltxid) { if (myGetTransaction(opentxid,opentx,hashBlock) != 0 && (numvouts=opentx.vout.size()) > 0 && - DecodeChannelsOpRet(opentx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain,version,confirmation) == 'O') + DecodeChannelsOpRet(opentx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain) == 'O') { Getscriptaddress(str,tx.vout[3].scriptPubKey); obj.push_back(Pair("Payment",tx.GetHash().GetHex().data())); @@ -929,11 +779,11 @@ UniValue ChannelsInfo(const CPubKey& pk,uint256 channeltxid) obj.push_back(Pair("Payments left",param1)); } } - else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3,tmpv,tmpc) == 'C' && opentxid==channeltxid) + else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'C' && opentxid==channeltxid) { obj.push_back(Pair("Close",tx.GetHash().GetHex().data())); } - else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3,tmpv,tmpc) == 'R' && opentxid==channeltxid) + else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'R' && opentxid==channeltxid) { Getscriptaddress(str,tx.vout[2].scriptPubKey); obj.push_back(Pair("Refund",tx.GetHash().GetHex().data())); diff --git a/src/cc/dapps/betdapp.c b/src/cc/dapps/betdapp.c deleted file mode 100644 index 911d98c429d..00000000000 --- a/src/cc/dapps/betdapp.c +++ /dev/null @@ -1,1572 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2020 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -// build betdapp and put in path: gcc cc/dapps/betdapp.c -lm -o betdapp; cp betdapp /usr/bin -// todo: -// log open and closed channels -// actually validate a channel -// and monitor channelinfo to see if the other side closed a channel -// make dorn game provable (commitment every minute, along with reveal of prior minute, 2 deep) - -#define DEXP2P_CHAIN ((char *)"DORN") -#define DEXP2P_PUBKEYS ((char *)"bet") -#define SUBATOMIC_DB "BETDAPP.DB" - -#include "dappinc.h" - - -#define SUBATOMIC_TIMEOUT 60 -#define SUBATOMIC_LOCKTIME 3600 -#define SUBATOMIC_TXFEE 10000 - -#define BETDAPP_MAXPAYMENTS 1000 - -#define SUBATOMIC_PRIORITY 5 - -#define SUBATOMIC_OPENREQUEST 1 -#define SUBATOMIC_APPROVED 2 -#define SUBATOMIC_OPENED 3 -#define SUBATOMIC_PAYMENT 4 -#define SUBATOMIC_PAIDINFULL 5 -#define SUBATOMIC_CLOSED 6 - -cJSON *SUBATOMIC_json; -int32_t SUBATOMIC_retval = -1; - -struct abinfo -{ - char pubkey[67],recvaddr[64],recvZaddr[128],secp[67]; -}; - -struct coininfo -{ - uint64_t satoshis,txfee,maxamount; - char istoken,iszaddr,isfile,isexternal,tokenid[65],coin[16],name[16],cli[256],acname[16],coinstr[16]; -}; - -struct msginfo -{ - UT_hash_handle hh; - bits256 bobpayment,alicepayment; - double price; - uint64_t gotpayment; - uint32_t paymentids[BETDAPP_MAXPAYMENTS],recvpaymentids[BETDAPP_MAXPAYMENTS]; - uint32_t origid,openrequestid,approvalid,openedid,paidid,closedid,locktime; - int32_t bobflag,status,numsentpayments,numrecvpayments; - char payload[128],approval[128],senderpub[67],openedtxidstr[65],closedtxidstr[65]; - struct coininfo base,rel; - struct abinfo alice,bob; -} *Messages; - -uint64_t subatomic_txfee(char *coin) -{ - return(SUBATOMIC_TXFEE); -} - -char *subatomic_checkname(char *tmpstr,struct msginfo *mp,int32_t baserel,char *coin) -{ - int32_t i,n; cJSON *external,*item; char *coinstr,*clistr; struct coininfo *ptr; - ptr = (baserel == 0) ? &mp->base : &mp->rel; - if ( coin[0] == 0 ) - return(coin); - if ( (external= jarray(&n,SUBATOMIC_json,"externalcoins")) != 0 && n > 0 ) - { - for (i=0; icli) ) - { - ptr->isexternal = 1; - strcpy(ptr->cli,clistr); - //fprintf(stderr,"found external coin %s %s\n",coin,clistr); - } - } - } - if ( coin[0] == '#' ) - { - strcpy(ptr->coinstr,coin); - strcpy(ptr->acname,""); - ptr->isfile = 1; - return(coin); - } - else if ( coin[0] != 'z' ) - { - for (i=1; coin[i]!=0; i++) - if ( coin[i] == '.' ) - { - dpow_tokenregister(ptr->tokenid,0,coin,0); - if ( ptr->tokenid[0] != 0 ) - { - strcpy(tmpstr,coin); - tmpstr[i] = 0; - //fprintf(stderr,"found a tokenmap %s -> %s %s\n",coin,tmpstr,ptr->tokenid); - ptr->istoken = 1; - strcpy(ptr->acname,coin); - strcpy(ptr->coinstr,""); - sprintf(ptr->cli,"komodo-cli -ac_name=%s",coin); - return(tmpstr); - } - } - if ( ptr->isexternal == 0 ) - { - if ( strcmp(coin,"KMD") != 0 ) - { - strcpy(ptr->acname,coin); - strcpy(ptr->coinstr,""); - sprintf(ptr->cli,"komodo-cli -ac_name=%s",coin); - } - else - { - strcpy(ptr->coinstr,coin); - strcpy(ptr->acname,""); - strcpy(ptr->cli,"komodo-cli"); - } - } - else - { - strcpy(ptr->coinstr,coin); - strcpy(ptr->acname,""); - } - return(coin); - } - else - { - for (i=1; coin[i]!=0; i++) - if ( isupper(coin[i]) == 0 ) - return(coin); - if ( strcmp(coin+1,"KMD") != 0 ) - ptr->iszaddr = 1; - return(coin+1); - } -} - -int32_t subatomic_zonly(struct coininfo *coin) -{ - if ( strcmp(coin->coin,"PIRATE") == 0 ) - return(1); - else return(coin->iszaddr); -} - -// //////////////////////////////// the four key functions needed to support a new item for subatomics - -bits256 _subatomic_sendrawtransaction(struct coininfo *coin,char *hexstr) -{ - char *retstr,str[65]; cJSON *retjson; bits256 txid; - memset(txid.bytes,0,sizeof(txid)); - if ( (retjson= subatomic_cli(coin->coin,&retstr,"sendrawtransaction",hexstr,"","","","","","")) != 0 ) - { - //fprintf(stderr,"broadcast.(%s)\n",jprint(retjson,0)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - if ( strlen(retstr) >= 64 ) - { - retstr[64] = 0; - decode_hex(txid.bytes,32,retstr); - } - fprintf(stderr,"_subatomic_sendrawtransaction %s txid.(%s)\n",coin->name,bits256_str(str,txid)); - free(retstr); - } - return(txid); -} - -int64_t _subatomic_getbalance(struct coininfo *coin) -{ - cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; - if ( (retjson= subatomic_cli(coin->coin,&retstr,"getbalance","","","","","","","")) != 0 ) - { - fprintf(stderr,"_subatomic_getbalance.(%s) %s returned json!\n",coin->coinstr,coin->cli); - free_json(retjson); - } - else if ( retstr != 0 ) - { - amount = atof(retstr) * SATOSHIDEN; - sprintf(cmpstr,"%.8f",dstr(amount)); - if ( strcmp(retstr,cmpstr) != 0 ) - amount++; - //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); - free(retstr); - } - return (amount); -} - -bits256 _subatomic_sendtoaddress(struct coininfo *coin,char *destaddr,int64_t satoshis) -{ - char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid; - memset(txid.bytes,0,sizeof(txid)); - sprintf(numstr,"%.8f",(double)satoshis/SATOSHIDEN); - if ( (retjson= subatomic_cli(coin->coin,&retstr,"sendtoaddress",destaddr,numstr,"false","","","","")) != 0 ) - { - fprintf(stderr,"unexpected _subatomic_sendtoaddress json.(%s)\n",jprint(retjson,0)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - if ( strlen(retstr) >= 64 ) - { - retstr[64] = 0; - decode_hex(txid.bytes,32,retstr); - } - fprintf(stderr,"_subatomic_sendtoaddress %s %.8f txid.(%s)\n",destaddr,(double)satoshis/SATOSHIDEN,bits256_str(str,txid)); - free(retstr); - } - return(txid); -} - -cJSON *_subatomic_rawtransaction(struct coininfo *coin,bits256 txid) -{ - cJSON *retjson; char *retstr,str[65]; - if ( (retjson= subatomic_cli(coin->coin,&retstr,"getrawtransaction",bits256_str(str,txid),"1","","","","","")) != 0 ) - { - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"_subatomic_rawtransaction.(%s) %s error.(%s)\n",coin->coin,coin->name,retstr); - free(retstr); - } - return(0); -} - -int64_t subatomic_getbalance(struct coininfo *coin) -{ - char *coinstr,*acname=""; FILE *fp; int64_t retval = 0; - if ( strcmp(coin->coin,"KMD") != 0 ) - { - acname = coin->coin; - coinstr = ""; - } else coinstr = coin->coin; - if ( coin->isfile != 0 ) - { - if ( (fp= fopen(coin->name+1,"rb")) != 0 ) // if alice, add bob pubkey to fname - { - fclose(fp); - retval = SATOSHIDEN; - } - return(retval); - } - else if ( subatomic_zonly(coin) != 0 ) - return(z_getbalance(coinstr,acname,DPOW_recvZaddr)); - else - { - if ( coin->istoken != 0 ) - { - if ( get_getbalance(coinstr,acname) < SUBATOMIC_TXFEE ) - { - fprintf(stderr,"not enough balance to send token\n"); - return(0); - } - //fprintf(stderr,"token balance %s\n",coin->tokenid); - return(get_tokenbalance(coinstr,acname,coin->tokenid) * SATOSHIDEN); - } - else if ( coin->isexternal == 0 ) - return(get_getbalance(coinstr,acname)); - else return(_subatomic_getbalance(coin)); - } -} - -bits256 subatomic_coinpayment(uint32_t origid,struct coininfo *coin,char *destaddr,uint64_t paytoshis,char *memostr,char *destpub,char *senderpub) -{ - bits256 txid; char opidstr[128],opretstr[32],str[65],*status,*coinstr,*acname=""; cJSON *retjson,*retjson2,*item,*res; int32_t i,pending=0; - memset(&txid,0,sizeof(txid)); - if ( coin->isfile != 0 ) - { - fprintf(stderr,"start broadcast of (%s)\n",coin->coin+1); - if ( (retjson= dpow_publish(SUBATOMIC_PRIORITY,coin->coin+1)) != 0 ) // spawn thread - { - sprintf(opretstr,"%08x",juint(retjson,"id")); - sprintf(opidstr,"%u",origid); - if ( (retjson2= dpow_broadcast(SUBATOMIC_PRIORITY,opretstr,"inbox",opidstr,senderpub,"","")) != 0 ) - free_json(retjson2); - fprintf(stderr,"broadcast file.(%s) and send id.%u to alice (%s)\n",coin->coin+1,juint(retjson,"id"),jprint(retjson,0)); - txid = jbits256(retjson,"filehash"); - free_json(retjson); - } - fprintf(stderr,"end broadcast of (%s) to %s\n",coin->coin+1,senderpub); - return(txid); - } - else if ( subatomic_zonly(coin) != 0 ) - { - if ( memostr[0] == 0 ) - memostr = "beef"; - z_sendmany(opidstr,"",coin->coin,DPOW_recvZaddr,destaddr,paytoshis,memostr); - for (i=0; icoin,opidstr)) != 0 ) - { - item = jitem(retjson,0); - if ( (status= jstr(item,"status")) != 0 ) - { - if ( strcmp(status,"executing") == 0 ) - pending++; - else - { - res = jobj(item,"result"); - txid = jbits256(res,"txid"); - //fprintf(stderr,"got Ztx txid.%s\n",bits256_str(str,txid)); - free_json(retjson); - break; - } - /*else if ( clearresults != 0 ) - { - if ( (result= z_getoperationresult(coinstr,"",jstri(array,i))) != 0 ) - { - free_json(result); - } - }*/ - } - free_json(retjson); - } - sleep(1); - } - if ( i == 60 ) - printf("%u timed out waiting for opid to finish\n",origid); - } - else - { - if ( strcmp(coin->coin,"KMD") != 0 ) - { - acname = coin->coin; - coinstr = ""; - } else coinstr = coin->coin; - if ( coin->istoken != 0 ) - txid = tokentransfer(coinstr,acname,coin->tokenid,destpub,paytoshis/SATOSHIDEN); - else if ( coin->isexternal == 0 ) - { - sprintf(opretstr,"%08x",origid); - txid = sendtoaddress(coinstr,acname,destaddr,paytoshis,opretstr); - } else txid = _subatomic_sendtoaddress(coin,destaddr,paytoshis); - printf("%u got txid.%s\n",origid,bits256_str(str,txid)); - } - return(txid); -} - -cJSON *subatomic_txidwait(struct coininfo *coin,bits256 txid,char *hexstr,int32_t numseconds,char *senderpub) -{ - int32_t i,zflag; char *coinstr,str[65],*acname=""; cJSON *rawtx; bits256 z; bits256 filehash; - memset(&z,0,sizeof(z)); - if ( memcmp(&z,&txid,sizeof(txid)) == 0 ) - return(0); - if ( hexstr != 0 && hexstr[0] != 0 ) // probably not worth doing and zaddr is a problem to decode - { - // compare against txid - // if matches, sendrawtransaction if OTC mode, decoode and return if channels mode - } - zflag = (subatomic_zonly(coin) != 0); - if ( strcmp(coin->coin,"KMD") != 0 ) - { - acname = coin->coin; - coinstr = ""; - } else coinstr = coin->coin; - for (i=0; iisfile != 0 ) - { - if ( (rawtx= dpow_subscribe(SUBATOMIC_PRIORITY,coin->coin+1,senderpub)) != 0 ) - { - filehash = jbits256(rawtx,"filehash"); - if ( memcmp(&filehash,&txid,sizeof(filehash)) != 0 ) - { - fprintf(stderr,"waiting (%s) (%s)\n",coin->coin+1,jprint(rawtx,0)); - free_json(rawtx); - rawtx = 0; - } else return(rawtx); - } - } - else if ( zflag != 0 ) - rawtx = get_z_viewtransaction(coinstr,acname,txid); - else if ( coin->isexternal == 0 ) - rawtx = get_rawtransaction(coinstr,acname,txid); - else rawtx = _subatomic_rawtransaction(coin,txid); - if ( rawtx != 0 ) - return(rawtx); - sleep(1); - } - printf("%s/%s timeout waiting for %s\n",coin->name,coin->coin,bits256_str(str,txid)); - return(0); -} - -int64_t subatomic_verifypayment(struct coininfo *coin,cJSON *rawtx,uint64_t destsatoshis,char *destaddr,bits256 txid) -{ - int32_t i,n,m,valid=0; bits256 tokenid,filehash,checkhash; cJSON *array,*item,*sobj,*a; char *addr,*acname,*coinstr,tokenaddr[64],*hex; uint8_t hexbuf[512],pub33[33]; uint64_t netval,recvsatoshis = 0; - if ( coin->isfile != 0 ) - { - filehash = jbits256(rawtx,"filehash"); - checkhash = jbits256(rawtx,"checkhash"); - if ( memcmp(&txid,&filehash,sizeof(txid)) == 0 && memcmp(&txid,&checkhash,sizeof(txid)) == 0 ) - { - fprintf(stderr,"verified file is matching the filehash (%s)\n",jprint(rawtx,0)); - return(SATOSHIDEN); - } else return(0); - } - else if ( subatomic_zonly(coin) != 0 ) - { - if ( (array= jarray(&n,rawtx,"outputs")) != 0 && n > 0 ) - { - for (i=0; iistoken != 0 ) - { - if ( (array= jarray(&n,rawtx,"vout")) != 0 && n > 0 ) - { - item = jitem(array,0); - if ( (sobj= jobj(item,"scriptPubKey")) != 0 && (a= jarray(&m,sobj,"addresses")) != 0 && m == 1 ) - { - if ( strcmp(coin->coin,"KMD") != 0 ) - { - acname = coin->coin; - coinstr = ""; - } else coinstr = coin->coin; - if ( get_tokenaddress(coinstr,acname,tokenaddr) != 0 ) - { - //fprintf(stderr,"tokenaddr.%s\n",tokenaddr); - if ( (addr= jstri(a,0)) != 0 && strcmp(addr,tokenaddr) == 0 ) - recvsatoshis += SATOSHIDEN * (uint64_t)(jdouble(item,"value")*SATOSHIDEN + 0.000000004999); - else fprintf(stderr,"miscompare (%s) vs %s\n",jprint(sobj,0),addr); - } - } - item = jitem(array,n-1); - if ( (sobj= jobj(item,"scriptPubKey")) != 0 && (hex= jstr(sobj,"hex")) != 0 && (m= is_hexstr(hex,0)) > 1 && m/2 < sizeof(hexbuf) ) - { - m >>= 1; - decode_hex(hexbuf,m,hex); - decode_hex(tokenid.bytes,32,coin->tokenid); - decode_hex(pub33,33,DPOW_secpkeystr); - // opret 69len EVAL_TOKENS 't' tokenid 1 33 pub33 - if ( hexbuf[0] == 0x6a && hexbuf[1] == 0x45 && hexbuf[2] == 0xf2 && hexbuf[3] == 't' && memcmp(&hexbuf[4],&tokenid,sizeof(tokenid)) == 0 && hexbuf[4+32] == 1 && hexbuf[4+32+1] == 33 && memcmp(&hexbuf[4+32+2],pub33,33) == 0 ) - { - valid = 1; - //fprintf(stderr,"validated it is a token transfer!\n"); - } else fprintf(stderr,"need to validate tokentransfer.(%s) %s %d\n",hex,DPOW_secpkeystr,memcmp(&hexbuf[4+32+2],pub33,33) == 0); - //6a 45 f2 74 2b1feef719ecb526b07416dd432bce603ac6dc8bfe794cddf105cb52f6aae3cd 01 21 02b27de3ee5335518b06f69f4fbabb029cfc737613b100996841d5532b324a5a61 - - } - recvsatoshis *= valid; - } - } - else - { - if ( (array= jarray(&n,rawtx,"vout")) != 0 && n > 0 ) - { - for (i=0; icoin,&retstr,"channelssecret",openedtxidstr,numstr,"","","","","")) != 0 ) - { - fprintf(stderr,"channelssecret (%s)\n",jprint(retjson,0)); - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"_subatomic_channelssecret.(%s) %s error.(%s)\n",coin->coin,coin->name,retstr); - free(retstr); - } - return(0); -} - -cJSON *_subatomic_channelspayment(struct coininfo *coin,char *openedtxidstr,int64_t amount,char *secret) -{ - cJSON *retjson; char *retstr,numstr[32]; - sprintf(numstr,"%llu",(long long)amount); - if ( (retjson= subatomic_cli(coin->coin,&retstr,"channelspayment",openedtxidstr,numstr,secret,"","","","")) != 0 ) - { - fprintf(stderr,"channelspayment (%s)\n",jprint(retjson,0)); - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"_subatomic_channelspayment.(%s) %s error.(%s)\n",coin->coin,coin->name,retstr); - free(retstr); - } - return(0); -} - -cJSON *_subatomic_channelsclose(struct coininfo *coin,char *openedtxidstr) -{ - cJSON *retjson; char *retstr; - if ( (retjson= subatomic_cli(coin->coin,&retstr,"channelsclose",openedtxidstr,"","","","","","")) != 0 ) - { - fprintf(stderr,"channelsclose (%s)\n",jprint(retjson,0)); - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"_subatomic_channelsclose.(%s) %s error.(%s)\n",coin->coin,coin->name,retstr); - free(retstr); - } - return(0); -} - -cJSON *_subatomic_channelsrefund(struct coininfo *coin,char *openedtxidstr,char *closetxidstr) -{ - cJSON *retjson; char *retstr; - if ( (retjson= subatomic_cli(coin->coin,&retstr,"channelsrefund",openedtxidstr,closetxidstr,"","","","","")) != 0 ) - { - fprintf(stderr,"channelsrefund (%s)\n",jprint(retjson,0)); - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"_subatomic_channelsrefund.(%s) %s error.(%s)\n",coin->coin,coin->name,retstr); - free(retstr); - } - return(0); -} - -cJSON *_subatomic_channelsinfo(struct coininfo *coin,char *openedtxidstr) -{ - cJSON *retjson; char *retstr; - if ( (retjson= subatomic_cli(coin->coin,&retstr,"channelsinfo",openedtxidstr,"","","","","","")) != 0 ) - { - fprintf(stderr,"channelsinfo (%s)\n",jprint(retjson,0)); - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"_subatomic_channelsinfo.(%s) %s error.(%s)\n",coin->coin,coin->name,retstr); - free(retstr); - } - return(0); -} - -cJSON *_subatomic_channelsopen(struct coininfo *coin,char *destpub,int32_t numpayments,int64_t paytoshis,char *tokenid) -{ - cJSON *retjson; char *retstr,str[65],numstr[32],paystr[32]; - sprintf(numstr,"%u",numpayments); - sprintf(paystr,"%llu",(long long)paytoshis); - if ( (retjson= subatomic_cli(coin->coin,&retstr,"channelsopen",destpub,numstr,paystr,tokenid,"","","")) != 0 ) - { - fprintf(stderr,"channelsopen (%s)\n",jprint(retjson,0)); - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"_subatomic_channelsopen.(%s) %s error.(%s)\n",coin->coin,coin->name,retstr); - free(retstr); - } - return(0); -} - -// -bits256 betdapp_channelsecret(struct coininfo *coin,char *openedtxidstr,int64_t totalpaid) -{ - cJSON *retjson; bits256 secret; - memset(secret.bytes,0,sizeof(secret)); - if ( (retjson= _subatomic_channelssecret(coin,openedtxidstr,totalpaid)) != 0 ) - { - secret = jbits256(retjson,"secret"); - free_json(retjson); - } - return(secret); -} - -bits256 betdapp_channelclose(struct coininfo *coin,char *openedtxidstr) -{ - cJSON *retjson; bits256 txid; char *hexstr; - memset(txid.bytes,0,sizeof(txid)); - if ( (retjson= _subatomic_channelsclose(coin,openedtxidstr)) != 0 ) - { - if ( (hexstr= jstr(retjson,"hex")) != 0 ) - txid = _subatomic_sendrawtransaction(coin,hexstr); - free_json(retjson); - } - return(txid); -} - - -int32_t betdapp_channelinfo(struct coininfo *coin,char *openedtxidstr) -{ - cJSON *retjson; char *errstr; int32_t retval = 0; - if ( (retjson= _subatomic_channelsinfo(coin,openedtxidstr)) != 0 ) - { - if ( (errstr= jstr(retjson,"error")) != 0 ) - { - retval = -1; - } - free_json(retjson); - } - return(retval); -} - -bits256 betdapp_channelrefund(struct coininfo *coin,char *openedtxidstr,char *closetxidstr) -{ - cJSON *retjson; bits256 txid; char *hexstr; - memset(txid.bytes,0,sizeof(txid)); - if ( (retjson= _subatomic_channelsrefund(coin,openedtxidstr,closetxidstr)) != 0 ) - { - if ( (hexstr= jstr(retjson,"hex")) != 0 ) - txid = _subatomic_sendrawtransaction(coin,hexstr); - free_json(retjson); - } - return(txid); -} - -bits256 betdapp_channelpayment(struct coininfo *coin,char *openedtxidstr,int64_t totalpaid,char *secret,int32_t broadcastflag) -{ - cJSON *retjson; char *hexstr,*errstr; bits256 txid; - memset(txid.bytes,0,sizeof(txid)); - if ( (retjson= _subatomic_channelspayment(coin,openedtxidstr,totalpaid,secret)) != 0 ) - { - if ( (errstr= jstr(retjson,"error")) != 0 ) - { - fprintf(stderr,"error with channelpayment\n"); - } - if ( (hexstr= jstr(retjson,"hex")) != 0 ) - { - if ( broadcastflag != 0 ) - txid = _subatomic_sendrawtransaction(coin,hexstr); - else - { - // check for no errors - fprintf(stderr,"make sure no errors\n"); - } - } - free_json(retjson); - } - return(txid); -} - -bits256 betdapp_channelopen(struct coininfo *coin,char *destpub,int32_t numpayments,int64_t paytoshis) -{ - cJSON *retjson; char *hexstr,*tokenid=""; bits256 txid; - memset(txid.bytes,0,sizeof(txid)); - if ( coin->istoken != 0 ) - tokenid = coin->tokenid; - if ( (retjson= _subatomic_channelsopen(coin,destpub,numpayments,paytoshis,tokenid)) != 0 ) - { - if ( (hexstr= jstr(retjson,"hex")) != 0 ) - txid = _subatomic_sendrawtransaction(coin,hexstr); - free_json(retjson); - } - return(txid); -} -// //////////// end - -struct msginfo *subatomic_find(uint32_t origid) -{ - struct msginfo *mp; - HASH_FIND(hh,Messages,&origid,sizeof(origid),mp); - return(mp); -} - -struct msginfo *subatomic_add(uint32_t origid) -{ - struct msginfo *mp = calloc(1,sizeof(*mp)); - mp->origid = origid; - HASH_ADD(hh,Messages,origid,sizeof(origid),mp); - return(mp); -} - -int32_t subatomic_status(struct msginfo *mp,int32_t status) -{ - static FILE *fp; - if ( fp == 0 ) - { - int32_t i,oid,s,n,num,count; struct msginfo *m; long fsize; - if ( (fp= fopen(SUBATOMIC_DB,"rb+")) == 0 ) - { - if ( (fp= fopen(SUBATOMIC_DB,"wb")) == 0 ) - { - fprintf(stderr,"cant create %s\n",SUBATOMIC_DB); - exit(-1); - } - } - else - { - fseek(fp,0,SEEK_END); - fsize = ftell(fp); - if ( (fsize % (sizeof(uint32_t)*2)) != 0 ) - { - fprintf(stderr,"%s illegal filesize.%ld\n",SUBATOMIC_DB,fsize); - exit(-1); - } - n = (int32_t)(fsize / (sizeof(uint32_t)*2)); - rewind(fp); - for (i=num=count=0; i SUBATOMIC_CLOSED ) - { - fprintf(stderr,"%s corrupted at filepos.%ld: illegal status.%d\n",SUBATOMIC_DB,ftell(fp),s); - exit(-1); - } - //fprintf(stderr,"%u <- %d\n",oid,s); - if ( (m= subatomic_find(oid)) == 0 ) - { - m = subatomic_add(oid); - count++; - } - if ( s > m->status ) - { - m->status = s; - num++; - } - } - fprintf(stderr,"initialized %d messages, updated %d out of total.%d\n",count,num,n); - } - } - if ( mp->status >= status ) - return(-1); - if ( fwrite(&mp->origid,1,sizeof(mp->origid),fp) != sizeof(mp->origid) || fwrite(&status,1,sizeof(status),fp) != sizeof(status) ) - fprintf(stderr,"error updating %s, risk of double spends\n",SUBATOMIC_DB); - fflush(fp); - mp->status = status; - return(0); -} - -struct msginfo *subatomic_tracker(uint32_t origid) -{ - struct msginfo *mp; - if ( (mp= subatomic_find(origid)) == 0 ) - { - mp = subatomic_add(origid); - subatomic_status(mp,0); - } - return(mp); -} - -char *subatomic_hexstr(char *jsonstr) -{ - char *hexstr; int32_t i,c,n = (int32_t)strlen(jsonstr); - hexstr = malloc(2*n + 3); - strcpy(hexstr,jsonstr); - for (i=0; iorigid); - jaddnum(item,"price",mp->price); - jaddnum(item,"openrequest",mp->openrequestid); - jaddstr(item,"base",mp->base.name); - jaddstr(item,"basecoin",mp->base.coin); - jadd64bits(item,"basesatoshis",mp->base.satoshis); - jadd64bits(item,"basetxfee",mp->base.txfee); - jadd64bits(item,"maxbaseamount",mp->base.maxamount); - jaddstr(item,"rel",mp->rel.name); - jaddstr(item,"relcoin",mp->rel.coin); - jadd64bits(item,"relsatoshis",mp->rel.satoshis); - jadd64bits(item,"reltxfee",mp->rel.txfee); - jadd64bits(item,"maxrelamount",mp->rel.maxamount); - jaddstr(item,"alice",mp->alice.pubkey); - jaddstr(item,"alicesecp",mp->alice.secp); - jaddstr(item,"bob",mp->bob.pubkey); - jaddstr(item,"bobsecp",mp->bob.secp); - if ( subatomic_zonly(&mp->rel) != 0 ) - jaddstr(item,"bobZaddr",mp->bob.recvZaddr); - else jaddstr(item,"bobaddr",mp->bob.recvaddr); - if ( mp->rel.istoken != 0 ) - jaddstr(item,"bobtoken",mp->rel.tokenid); - if ( subatomic_zonly(&mp->base) != 0 ) - jaddstr(item,"aliceZaddr",mp->alice.recvZaddr); - else jaddstr(item,"aliceaddr",mp->alice.recvaddr); - if ( mp->base.istoken != 0 ) - jaddstr(item,"alicetoken",mp->base.tokenid); - return(item); -} - -uint64_t subatomic_orderbook_mpset(struct msginfo *mp,char *basecheck) -{ - cJSON *retjson; char *tagA,*tagB,*senderpub,*str,tmpstr[32]; int32_t matches=0; double volA,volB; int64_t txfee=0; - strcpy(mp->base.name,basecheck); - strcpy(mp->base.coin,subatomic_checkname(tmpstr,mp,0,basecheck)); - mp->rel.txfee = subatomic_txfee(mp->rel.coin); - if ( (retjson= dpow_get(mp->origid)) != 0 ) - { - //fprintf(stderr,"dpow_get.(%s) (%s/%s)\n",jprint(retjson,0),mp->base.coin,mp->rel.coin); - if ( (senderpub= jstr(retjson,"senderpub")) != 0 && is_hexstr(senderpub,0) == 66 && (tagA= jstr(retjson,"tagA")) != 0 && (tagB= jstr(retjson,"tagB")) != 0 && strncmp(tagB,mp->rel.name,strlen(mp->rel.name)) == 0 && strlen(tagA) < sizeof(mp->base.name) ) - { - strcpy(mp->base.name,tagA); - strcpy(mp->base.coin,subatomic_checkname(tmpstr,mp,0,tagA)); - if ( basecheck[0] == 0 || strncmp(basecheck,tagA,strlen(basecheck)) == 0 ) - matches = 1; - else if ( strcmp(tagA,mp->base.name) == 0 ) - matches = 1; - else if ( mp->bobflag != 0 && tagA[0] == '#' && strcmp(mp->base.name,"#allfiles") == 0 ) - matches = 1; - if ( matches != 0 ) - { - if ( (str= jstr(retjson,"decrypted")) != 0 && strlen(str) < 128 ) - strcpy(mp->payload,str); - mp->locktime = juint(retjson,"timestamp") + SUBATOMIC_LOCKTIME; - mp->base.txfee = subatomic_txfee(mp->base.coin); - strcpy(mp->senderpub,senderpub); - volB = jdouble(retjson,"amountB"); - volA = jdouble(retjson,"amountA"); - mp->base.maxamount = volA*SATOSHIDEN + 0.0000000049999; - mp->rel.maxamount = volB*SATOSHIDEN + 0.0000000049999; - mp->base.maxamount = (mp->base.maxamount/BETDAPP_MAXPAYMENTS) * BETDAPP_MAXPAYMENTS; - mp->rel.maxamount = (mp->rel.maxamount/BETDAPP_MAXPAYMENTS) * BETDAPP_MAXPAYMENTS; - mp->rel.satoshis = (mp->rel.satoshis/BETDAPP_MAXPAYMENTS) * BETDAPP_MAXPAYMENTS; - if ( 0 && mp->rel.istoken == 0 ) - txfee = mp->rel.txfee; - if ( mp->base.maxamount != 0 && mp->rel.maxamount != 0 && volA > SMALLVAL && volB > SMALLVAL && mp->rel.satoshis <= mp->rel.maxamount ) - { - mp->price = volA / volB; - mp->base.satoshis = (mp->rel.satoshis - txfee) * mp->price; - mp->base.satoshis = (mp->base.satoshis/BETDAPP_MAXPAYMENTS) * BETDAPP_MAXPAYMENTS; - //fprintf(stderr,"base satoshis.%llu\n",(long long)mp->base.satoshis); - } else fprintf(stderr,"%u rel %llu vs (%llu %llu)\n",mp->origid,(long long)mp->rel.satoshis,(long long)mp->base.maxamount,(long long)mp->rel.maxamount); - } else printf("%u didnt match (%s) tagA.%s %s, tagB.%s %s %d %d\n",mp->origid,basecheck,tagA,mp->base.name,tagB,mp->rel.name,tagA[0] == '#', strcmp(mp->base.name,"#allfiles") == 0); - } else printf("%u didnt compare tagA.%s %s, tagB.%s %s\n",mp->origid,tagA,mp->base.name,tagB,mp->rel.name); - free_json(retjson); - } - return(mp->base.satoshis); -} - -char *randhashstr(char *str) -{ - bits256 rands; int32_t i; - for (i=0; i<32; i++) - rands.bytes[i] = rand() >> 17; - bits256_str(str,rands); - return(str); -} - -void subatomic_extrafields(cJSON *dest,cJSON *src) -{ - char *str; - if ( (str= jstr(src,"approval")) != 0 ) - jaddstr(dest,"approval",str); - if ( (str= jstr(src,"opened")) != 0 ) - jaddstr(dest,"opened",str); - if ( (str= jstr(src,"payamount")) != 0 ) - jaddstr(dest,"payamount",str); - if ( (str= jstr(src,"destaddr")) != 0 ) - jaddstr(dest,"destaddr",str); - if ( (str= jstr(src,"bobpayment")) != 0 ) - jaddstr(dest,"bobpayment",str); - if ( (str= jstr(src,"alicepayment")) != 0 ) - jaddstr(dest,"alicepayment",str); - if ( (str= jstr(src,"bobaddr")) != 0 ) - jaddstr(dest,"bobaddr",str); - if ( (str= jstr(src,"bobZaddr")) != 0 ) - jaddstr(dest,"bobZaddr",str); - if ( (str= jstr(src,"aliceaddr")) != 0 ) - jaddstr(dest,"aliceaddr",str); - if ( (str= jstr(src,"aliceZaddr")) != 0 ) - jaddstr(dest,"aliceZaddr",str); - if ( (str= jstr(src,"alicetoken")) != 0 ) - jaddstr(dest,"alicetoken",str); - if ( (str= jstr(src,"bobtoken")) != 0 ) - jaddstr(dest,"bobtoken",str); -} - -char *subatomic_submit(cJSON *argjson,int32_t tobob) -{ - char *jsonstr,*hexstr; - jaddnum(argjson,"tobob",tobob != 0); - jsonstr = jprint(argjson,1); - hexstr = subatomic_hexstr(jsonstr); - free(jsonstr); - return(hexstr); -} - -int32_t subatomic_approved(struct msginfo *mp,cJSON *approval,cJSON *msgjson,char *senderpub) -{ - char *hexstr,numstr[32],redeemscript[1024],*coin,*acname=""; cJSON *retjson,*decodejson; int32_t i,retval = 0; - subatomic_extrafields(approval,msgjson); - sprintf(numstr,"%u",mp->origid); - for (i=0; numstr[i]!=0; i++) - sprintf(&mp->approval[i<<1],"%02x",numstr[i]); - sprintf(&mp->approval[i<<1],"%02x",' '); - i++; - mp->approval[i<<1] = 0; - jaddstr(approval,"approval",mp->approval); - hexstr = subatomic_submit(approval,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"approved",senderpub,"","")) != 0 ) - { - if ( (mp->approvalid= juint(retjson,"id")) != 0 ) - retval = 1; - printf("%u approvalid.%u (%s)\n",mp->origid,mp->approvalid,senderpub); - subatomic_status(mp,SUBATOMIC_APPROVED); - free_json(retjson); - } - free(hexstr); - return(retval); -} - -int32_t subatomic_opened(struct msginfo *mp,cJSON *opened,cJSON *msgjson,char *senderpub,char *destsecppub,int64_t mult) -{ - char *hexstr; bits256 opentxid; cJSON *retjson; struct coininfo *coin; int32_t retval = 0; - if ( mp->bobflag == 0 ) - coin = &mp->rel; - else coin = &mp->base; - opentxid = betdapp_channelopen(coin,destsecppub,BETDAPP_MAXPAYMENTS,(mult * coin->satoshis)/BETDAPP_MAXPAYMENTS); - jaddstr(opened,"opened",bits256_str(mp->openedtxidstr,opentxid)); - hexstr = subatomic_submit(opened,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"opened",senderpub,"","")) != 0 ) - { - if ( (mp->openedid= juint(retjson,"id")) != 0 ) - retval = 1; - printf("%u openedid.%u\n",mp->origid,mp->openedid); - subatomic_status(mp,SUBATOMIC_OPENED); - free_json(retjson); - } - free(hexstr); - return(retval); -} - -int32_t subatomic_payment(struct msginfo *mp,cJSON *payment,cJSON *msgjson,char *senderpub) -{ - bits256 txid; uint64_t paytoshis; cJSON *retjson; char numstr[32],*coin,*dest,*hexstr; int32_t retval = 0; - if ( mp->bobflag == 0 ) - { - coin = mp->rel.name; - paytoshis = mp->rel.satoshis; - if ( subatomic_zonly(&mp->rel) != 0 ) - dest = mp->bob.recvZaddr; - else dest = mp->bob.recvaddr; - sprintf(numstr,"%llu",(long long)paytoshis); - jaddstr(payment,"alicepays",numstr); - jaddstr(payment,"bobdestaddr",dest); - txid = subatomic_coinpayment(mp->origid,&mp->rel,dest,paytoshis,mp->approval,mp->bob.secp,senderpub); - jaddbits256(payment,"alicepayment",txid); - mp->alicepayment = txid; - hexstr = 0; // get it from rawtransaction of txid - jaddstr(payment,"alicetx",hexstr); - } - else - { - coin = mp->base.name; - paytoshis = mp->base.satoshis; - if ( subatomic_zonly(&mp->base) != 0 ) - dest = mp->alice.recvZaddr; - else dest = mp->alice.recvaddr; - sprintf(numstr,"%llu",(long long)paytoshis); - jaddstr(payment,"bobpays",numstr); - jaddstr(payment,"alicedestaddr",dest); - txid = subatomic_coinpayment(mp->origid,&mp->base,dest,paytoshis,mp->approval,mp->alice.secp,senderpub); - jaddbits256(payment,"bobpayment",txid); - mp->bobpayment = txid; - hexstr = 0; // get it from rawtransaction of txid - jaddstr(payment,"bobtx",hexstr); - } - hexstr = subatomic_submit(payment,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"payment",senderpub,"","")) != 0 ) - { - if ( (mp->paymentids[0]= juint(retjson,"id")) != 0 ) - retval = 1; - printf("%u: %.8f %s -> %s, paymentid[%d] %u\n",mp->origid,dstr(paytoshis),coin,dest,mp->numsentpayments,mp->paymentids[mp->numsentpayments]); - subatomic_status(mp,SUBATOMIC_PAYMENT); - free_json(retjson); - } - free(hexstr); - return(retval); -} - -int32_t subatomic_paidinfull(struct msginfo *mp,cJSON *paid,cJSON *msgjson,char *senderpub) -{ - char *hexstr; cJSON *retjson; int32_t retval = 0; - jaddstr(paid,"paid","in full"); - subatomic_extrafields(paid,msgjson); - hexstr = subatomic_submit(paid,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"paid",senderpub,"","")) != 0 ) - { - if ( (mp->paidid= juint(retjson,"id")) != 0 ) - retval = 1; - printf("%u paidid.%u\n",mp->origid,mp->paidid); - subatomic_status(mp,SUBATOMIC_PAIDINFULL); - free_json(retjson); - } - free(hexstr); - return(retval); -} - -int32_t subatomic_closed(struct msginfo *mp,cJSON *closed,cJSON *msgjson,char *senderpub) -{ - char *hexstr; bits256 txid; cJSON *retjson; struct coininfo *coin; int32_t retval = 0; - coin = (mp->bobflag != 0) ? &mp->rel : &mp->base; - txid = betdapp_channelclose(coin,mp->openedtxidstr); - bits256_str(mp->closedtxidstr,txid); - jaddstr(closed,"closed",mp->closedtxidstr); - subatomic_extrafields(closed,msgjson); - hexstr = subatomic_submit(closed,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"closed",senderpub,"","")) != 0 ) - { - if ( (mp->closedid= juint(retjson,"id")) != 0 ) - retval = 1; - subatomic_status(mp,SUBATOMIC_CLOSED); - printf("%u closedid.%u\n",mp->origid,mp->closedid); - free_json(retjson); - } - free(hexstr); - return(retval); -} - -uint32_t subatomic_alice_openrequest(struct msginfo *origmp) -{ - struct msginfo *mp; cJSON *retjson,*openrequest; char *hexstr,*str,tmpstr[32]; - mp = subatomic_tracker(origmp->origid); - mp->origid = origmp->origid; - mp->rel.satoshis = origmp->rel.satoshis; - mp->rel.istoken = origmp->rel.istoken; - strcpy(mp->rel.tokenid,origmp->rel.tokenid); - strcpy(mp->rel.name,origmp->rel.name); - strcpy(mp->rel.coin,subatomic_checkname(tmpstr,mp,1,origmp->rel.name)); - strcpy(mp->alice.pubkey,DPOW_pubkeystr); - strcpy(mp->alice.secp,DPOW_secpkeystr); - strcpy(mp->alice.recvZaddr,DPOW_recvZaddr); - strcpy(mp->alice.recvaddr,DPOW_recvaddr); - printf("CHECK rel.%s/%s %s openrequest %u status.%d (%s/%s)\n",mp->rel.name,mp->rel.coin,mp->rel.tokenid,mp->origid,mp->status,mp->alice.recvaddr,mp->alice.recvZaddr); - if ( mp->status == 0 && subatomic_orderbook_mpset(mp,"") != 0 ) - { - strcpy(mp->bob.pubkey,mp->senderpub); - strcpy(origmp->base.name,mp->base.name); - strcpy(origmp->base.coin,mp->base.coin); - origmp->base.istoken = mp->base.istoken; - strcpy(origmp->base.tokenid,mp->base.tokenid); - fprintf(stderr,"checks\n"); - if ( mp->rel.istoken != 0 && ((mp->rel.satoshis % SATOSHIDEN) != 0 || mp->rel.iszaddr != 0) ) - { - printf("%u cant do zaddr or fractional rel %s.%s tokens %.8f\n",mp->origid,mp->rel.coin,mp->rel.tokenid,dstr(mp->rel.satoshis)); - return(0); - } - else if ( mp->base.istoken != 0 && ((mp->base.satoshis % SATOSHIDEN) != 0 || mp->base.iszaddr != 0 ) ) - { - printf("%u cant do zaddr or fractional base %s.%s tokens %.8f\n",mp->origid,mp->base.coin,mp->base.tokenid,dstr(mp->base.satoshis)); - return(0); - } - else if ( (openrequest= subatomic_mpjson(mp)) != 0 ) - { - hexstr = subatomic_submit(openrequest,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"openrequest",mp->bob.pubkey,"","")) != 0 ) - { - mp->openrequestid = juint(retjson,"id"); - printf("%u openrequest.%u -> (%s)\n",mp->origid,mp->openrequestid,mp->bob.pubkey); - subatomic_status(mp,SUBATOMIC_OPENREQUEST); - free_json(retjson); - } - free(hexstr); - } - } - return(mp->openrequestid); -} - -void subatomic_bob_gotopenrequest(uint32_t inboxid,char *senderpub,cJSON *msgjson,char *basename,char *relname) -{ - struct msginfo *mp; cJSON *approval; int32_t origid; char *addr,tmpstr[32],*coin,*acname=""; - origid = juint(msgjson,"origid"); - mp = subatomic_tracker(origid); - strcpy(mp->base.name,basename); - strcpy(mp->base.coin,subatomic_checkname(tmpstr,mp,0,basename)); - strcpy(mp->rel.name,relname); - strcpy(mp->rel.coin,subatomic_checkname(tmpstr,mp,1,relname)); - mp->origid = origid; - mp->rel.satoshis = j64bits(msgjson,"relsatoshis"); - mp->bobflag = 1; - strcpy(mp->bob.pubkey,DPOW_pubkeystr); - strcpy(mp->bob.secp,DPOW_secpkeystr); - strcpy(mp->bob.recvZaddr,DPOW_recvZaddr); - strcpy(mp->bob.recvaddr,DPOW_recvaddr); - if ( (addr= jstr(msgjson,"aliceaddr")) != 0 ) - strcpy(mp->alice.recvaddr,addr); - if ( (addr= jstr(msgjson,"aliceZaddr")) != 0 ) - strcpy(mp->alice.recvZaddr,addr); - if ( (addr= jstr(msgjson,"alicesecp")) != 0 ) - strcpy(mp->alice.secp,addr); - printf("%u got open request\n",mp->origid); - if ( mp->status == 0 && subatomic_orderbook_mpset(mp,basename) != 0 && (approval= subatomic_mpjson(mp)) != 0 ) - { - if ( mp->rel.istoken != 0 && ((mp->rel.satoshis % SATOSHIDEN) != 0 || mp->rel.iszaddr != 0) ) - { - printf("%u cant do zaddr or fractional rel %s.%s tokens %.8f\n",mp->origid,mp->rel.coin,mp->rel.tokenid,dstr(mp->rel.satoshis)); - subatomic_closed(mp,approval,msgjson,senderpub); - return; - } - else if ( mp->base.istoken != 0 && ((mp->base.satoshis % SATOSHIDEN) != 0 || mp->base.iszaddr != 0 ) ) - { - printf("%u cant do zaddr or fractional base %s.%s tokens %.8f\n",mp->origid,mp->base.coin,mp->base.tokenid,dstr(mp->base.satoshis)); - subatomic_closed(mp,approval,msgjson,senderpub); - return; - } - else if ( subatomic_getbalance(&mp->base) < mp->base.satoshis ) - { - printf("%u bob node low on %s funds! %.8f not enough for %.8f\n",mp->origid,mp->base.coin,dstr(subatomic_getbalance(&mp->base)),dstr(mp->base.satoshis)); - subatomic_closed(mp,approval,msgjson,senderpub); - } - else - { - printf("%u bob (%s/%s) gotopenrequest origid.%u status.%d (%s/%s) SENDERPUB.(%s)\n",mp->origid,mp->base.name,mp->rel.name,mp->origid,mp->status,mp->bob.recvaddr,mp->bob.recvZaddr,senderpub); - subatomic_approved(mp,approval,msgjson,senderpub); - } - } -} - -int32_t subatomic_channelapproved(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) -{ - struct msginfo *mp; cJSON *approval; char *addr,*coin,*acname; int32_t retval = 0; - mp = subatomic_tracker(juint(msgjson,"origid")); - if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (approval= subatomic_mpjson(mp)) != 0 ) - { - printf("%u iambob.%d (%s/%s) channelapproved origid.%u status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->origid,mp->status); - if ( mp->bobflag == 0 && mp->status == SUBATOMIC_OPENREQUEST ) - { - if ( (addr= jstr(msgjson,"bobaddr")) != 0 ) - strcpy(mp->bob.recvaddr,addr); - if ( (addr= jstr(msgjson,"bobZaddr")) != 0 ) - strcpy(mp->bob.recvZaddr,addr); - if ( (addr= jstr(msgjson,"bobsecp")) != 0 ) - strcpy(mp->bob.secp,addr); - retval = subatomic_opened(mp,approval,msgjson,senderpub,mp->bob.secp,1); - } - else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_APPROVED ) - retval = 1; // nothing to do subatomic_opened(mp,approval,msgjson,senderpub); - } - return(retval); -} - -int32_t betdapp_payment(struct msginfo *mp,cJSON *payment,cJSON *msgjson,char *senderpub,int64_t paytoshis) -{ - bits256 txid; cJSON *retjson; int64_t incr,totalpaid = 0; struct coininfo *coin; char numstr[32],*dest,*hexstr; int32_t numpayments,retval = 0; - if ( mp->bobflag == 0 ) - coin = &mp->rel; - else coin = &mp->base; - incr = (coin->satoshis / BETDAPP_MAXPAYMENTS); - numpayments = paytoshis / incr; - totalpaid = (mp->numsentpayments + numpayments) * incr; - if ( mp->bobflag == 0 ) - { - dest = mp->bob.recvaddr; - sprintf(numstr,"%llu",(long long)paytoshis); - jaddstr(payment,"alicepays",numstr); - jaddstr(payment,"bobdestaddr",dest); - txid = betdapp_channelpayment(coin,mp->openedtxidstr,totalpaid,"",0); - jaddbits256(payment,"alicepayment",txid); - mp->alicepayment = txid; - hexstr = 0; // get it from rawtransaction of txid - jaddstr(payment,"alicetx",hexstr); - } - else - { - dest = mp->alice.recvaddr; - sprintf(numstr,"%llu",(long long)paytoshis); - jaddstr(payment,"bobpays",numstr); - jaddstr(payment,"alicedestaddr",dest); - txid = betdapp_channelpayment(coin,mp->openedtxidstr,totalpaid,"",0); - jaddbits256(payment,"bobpayment",txid); - mp->bobpayment = txid; - hexstr = 0; // get it from rawtransaction of txid - jaddstr(payment,"bobtx",hexstr); - } - hexstr = subatomic_submit(payment,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"payment",senderpub,"","")) != 0 ) - { - if ( (mp->paymentids[mp->numsentpayments]= juint(retjson,"id")) != 0 ) - { - retval = 1; - mp->numsentpayments++; - } - printf("%u: %.8f %s -> %s, paymentid[%d] %u\n",mp->origid,dstr(paytoshis),coin->name,dest,mp->numsentpayments-1,mp->paymentids[mp->numsentpayments-1]); - subatomic_status(mp,SUBATOMIC_PAYMENT); - free_json(retjson); - } - free(hexstr); - return(retval); -} - -int32_t betdapp_paymentvalidate(struct msginfo *mp,cJSON *msgjson) -{ - // extract secret, validate against last payment - // update mp->numrecvpayments - return(1); -} - -int64_t bob_payoutcalc(struct msginfo *mp,cJSON *msgjson) -{ - int64_t betsize = mp->base.satoshis / BETDAPP_MAXPAYMENTS; - //fprintf(stderr,"betsize %.8f\n",dstr(betsize)); - // verify game type - // apply game logic - return(2 * betsize * ((rand()>>17) & 1)); -} - -int32_t alice_gameplay(struct msginfo *mp,cJSON *argjson,cJSON *msgjson,char *senderpub,int32_t type) -{ - int64_t betsize = mp->base.satoshis / BETDAPP_MAXPAYMENTS; int32_t retval = 0; - fprintf(stderr,"numsent.%d betsize %.8f\n",mp->numsentpayments,dstr(betsize)); - if ( mp->numsentpayments < 10 ) - { - jaddstr(argjson,"game","dorn"); - retval = betdapp_payment(mp,argjson,msgjson,senderpub,betsize); // winnings come in via payment, losses via fullypaid - } else retval = subatomic_closed(mp,argjson,msgjson,senderpub); - return(retval); -} - -int32_t subatomic_incomingopened(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) -{ - struct msginfo *mp; cJSON *argjson; int32_t retval = 0; - mp = subatomic_tracker(juint(msgjson,"origid")); - if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (argjson= subatomic_mpjson(mp)) != 0 ) - { - printf("%u iambob.%d (%s/%s) incomingchannel status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); - if ( mp->bobflag == 0 && mp->status == SUBATOMIC_OPENED ) - retval = alice_gameplay(mp,argjson,msgjson,senderpub,-1); - else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_APPROVED ) - retval = subatomic_opened(mp,argjson,msgjson,senderpub,mp->alice.secp,2); // 2x for dorn - } - return(retval); -} - -int32_t subatomic_incomingpayment(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) -{ - static FILE *fp; - struct msginfo *mp; cJSON *argjson,*rawtx,*retjson; bits256 txid; char str[65],*hexstr; int32_t validated=0,retval = 0; int64_t payout=0; - mp = subatomic_tracker(juint(msgjson,"origid")); - if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (argjson= subatomic_mpjson(mp)) != 0 ) - { - printf("%u iambob.%d (%s/%s) incomingpayment status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); - if ( (validated= betdapp_paymentvalidate(mp,msgjson)) < 0 ) - { - fprintf(stderr,"received invalid payment, close the channel\n"); - subatomic_closed(mp,argjson,msgjson,senderpub); - } - if ( mp->bobflag == 0 ) - { - retval = alice_gameplay(mp,argjson,msgjson,senderpub,1); - } - else - { - if ( (payout= bob_payoutcalc(mp,msgjson)) > 0 ) - retval = betdapp_payment(mp,argjson,msgjson,senderpub,payout); - else retval = subatomic_paidinfull(mp,argjson,msgjson,senderpub); - } - } - return(retval); -} - -int32_t subatomic_incomingfullypaid(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) -{ - struct msginfo *mp; cJSON *argjson; int32_t retval = 0; - mp = subatomic_tracker(juint(msgjson,"origid")); - if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (argjson= subatomic_mpjson(mp)) != 0 ) - { - printf("%u iambob.%d (%s/%s) incomingfullypaid status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); - // error check msgjson vs M - if ( mp->bobflag == 0 && (mp->status == SUBATOMIC_PAYMENT || mp->status == SUBATOMIC_PAIDINFULL) ) - retval = alice_gameplay(mp,argjson,msgjson,senderpub,0); - else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_PAYMENT ) - retval = subatomic_closed(mp,argjson,msgjson,senderpub); - } - return(retval); -} - -int32_t subatomic_incomingclosed(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) -{ - struct msginfo *mp; cJSON *argjson; int32_t retval = 0; - mp = subatomic_tracker(juint(msgjson,"origid")); - if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (argjson= subatomic_mpjson(mp)) != 0 ) - { - printf("%u iambob.%d (%s/%s) incomingclose status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); - if ( mp->bobflag != 0 ) - dpow_cancel(mp->origid); - if ( mp->status < SUBATOMIC_CLOSED ) - { - retval = subatomic_closed(mp,argjson,msgjson,senderpub); - subatomic_status(mp,SUBATOMIC_CLOSED); - } - retval = 1; - SUBATOMIC_retval = 0; - } - return(retval); -} - -int32_t subatomic_ismine(int32_t bobflag,cJSON *json,char *basename,char *relname) -{ - char *base,*rel; - if ( (base= jstr(json,"base")) != 0 && (rel= jstr(json,"rel")) != 0 ) - { - if ( strcmp(base,basename) == 0 && strcmp(rel,relname) == 0 ) - return(1); - if ( bobflag != 0 ) - { - if ( strcmp(basename,"#allfiles") == 0 && base[0] == '#' ) - return(1); - fprintf(stderr,"skip ismine (%s/%s) vs (%s/%s)\n",basename,relname,base,rel); - } - } - return(0); -} - -void subatomic_tokensregister(int32_t priority) -{ - char *token_name,*tokenid,existing[65]; cJSON *tokens,*token; int32_t i,numtokens; - if ( SUBATOMIC_json != 0 && (tokens= jarray(&numtokens,SUBATOMIC_json,"tokens")) != 0 ) - { - for (i=0; i 0 ) - { - for (j=0; j %s, %u %llu %u\n",mp->bobflag,mp->base.name,mp->rel.name,mp->origid,(long long)mp->rel.satoshis,mp->openrequestid); - while ( 1 ) - { - if ( msgs == 0 ) - { - sleep(1); - fflush(stdout); - if ( mp->bobflag != 0 ) - { - dpow_pubkeyregister(SUBATOMIC_PRIORITY); - //subatomic_tokensregister(SUBATOMIC_PRIORITY); - //subatomic_filesregister(SUBATOMIC_PRIORITY); - } - } - msgs = 0; - for (iter=0; iter<(int32_t)(sizeof(tagBs)/sizeof(*tagBs)); iter++) - { - tagB = tagBs[iter]; - if ( (ptrs= dpow_inboxcheck(&n,&stopats[iter],tagB)) != 0 ) - { - for (i=0; ijsonstr)) != 0 ) - { - if ( jint(inboxjson,"tobob") != mp->bobflag ) - continue; - if ( subatomic_ismine(mp->bobflag,inboxjson,mp->base.name,mp->rel.name) != 0 ) - { - if ( strcmp(tagB,"openrequest") == 0 && mp->bobflag != 0 ) - subatomic_bob_gotopenrequest(ptr->shorthash,ptr->senderpub,inboxjson,mp->base.name,mp->rel.name); - else if ( strcmp(tagB,"approved") == 0 ) - mask |= subatomic_channelapproved(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 0; - else if ( strcmp(tagB,"opened") == 0 ) - mask |= subatomic_incomingopened(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 1; - else if ( strcmp(tagB,"payment") == 0 ) - mask |= subatomic_incomingpayment(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 2; - else if ( strcmp(tagB,"paid") == 0 ) - mask |= subatomic_incomingfullypaid(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 3; - else if ( strcmp(tagB,"closed") == 0 ) - mask |= subatomic_incomingclosed(ptr->shorthash,ptr->senderpub,inboxjson,mp) * 0x1f; - else fprintf(stderr,"iambob.%d unknown unexpected tagB.(%s)\n",mp->bobflag,tagB); - } - free_json(inboxjson); - } else fprintf(stderr,"subatomic iambob.%d loop got unparseable(%s)\n",mp->bobflag,ptr->jsonstr); - free(ptr); - ptrs[i] = 0; - } - } - free(ptrs); - } - } - if ( mp->bobflag == 0 && (mask & 0x1f) == 0x1f ) - { - printf("alice %u %llu %u finished\n",mp->origid,(long long)mp->rel.satoshis,mp->openrequestid); - break; - } - } -} - -int32_t main(int32_t argc,char **argv) -{ - char *fname = "subatomic.json"; - int32_t i,height; char *coin,*kcli,*subatomic,*hashstr,*acname=(char *)""; cJSON *retjson; bits256 blockhash; char checkstr[65],str[65],str2[65],tmpstr[32]; long fsize; struct msginfo M; - memset(&M,0,sizeof(M)); - srand((int32_t)time(NULL)); - if ( (subatomic= filestr(&fsize,fname)) == 0 ) - { - fprintf(stderr,"cant load %s file\n",fname); - exit(-1); - } - if ( (SUBATOMIC_json= cJSON_Parse(subatomic)) == 0 ) - { - fprintf(stderr,"cant parse subatomic.json file (%s)\n",subatomic); - exit(-1); - } - free(subatomic); - if ( argc >= 4 ) - { - if ( dpow_pubkey() < 0 ) - { - fprintf(stderr,"couldnt set pubkey for DEX\n"); - return(-1); - } - coin = (char *)argv[1]; - if ( argv[2][0] != 0 ) - REFCOIN_CLI = (char *)argv[2]; - else - { - if ( strcmp(coin,"KMD") != 0 ) - { - acname = coin; - } - } - hashstr = (char *)argv[3]; - strcpy(M.rel.coin,subatomic_checkname(tmpstr,&M,1,coin)); - strcpy(M.rel.name,coin); - if ( argc == 4 && strlen(hashstr) == 64 ) // for blocknotify usage, seems not needed - { - height = get_coinheight(coin,acname,&blockhash); - bits256_str(checkstr,blockhash); - if ( strcmp(checkstr,hashstr) == 0 ) - { - fprintf(stderr,"%s: (%s) %s height.%d\n",coin,REFCOIN_CLI!=0?REFCOIN_CLI:"",checkstr,height); - if ( (retjson= dpow_ntzdata(coin,SUBATOMIC_PRIORITY,height,blockhash)) != 0 ) - free_json(retjson); - } else fprintf(stderr,"coin.%s (%s) %s vs %s, height.%d\n",coin,REFCOIN_CLI!=0?REFCOIN_CLI:"",checkstr,hashstr,height); - if ( strcmp("BTC",coin) != 0 ) - { - bits256 prevntzhash,ntzhash; int32_t prevntzheight,ntzheight; uint32_t ntztime,prevntztime; char hexstr[81]; cJSON *retjson2; - prevntzhash = dpow_ntzhash(coin,&prevntzheight,&prevntztime); - if ( (retjson= get_getinfo(coin,acname)) != 0 ) - { - ntzheight = juint(retjson,"notarized"); - ntzhash = jbits256(retjson,"notarizedhash"); - if ( ntzheight > prevntzheight ) - { - get_coinmerkleroot(coin,acname,ntzhash,&ntztime); - fprintf(stderr,"NOTARIZATION %s.%d %s t.%u\n",coin,ntzheight,bits256_str(str,ntzhash),ntztime); - bits256_str(hexstr,ntzhash); - sprintf(&hexstr[64],"%08x",ntzheight); - sprintf(&hexstr[72],"%08x",ntztime); - hexstr[80] = 0; - if ( (retjson2= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,coin,"notarizations",DPOW_pubkeystr,"","")) != 0 ) - free_json(retjson2); - } - else if ( ntzheight == prevntzheight && memcmp(&prevntzhash,&ntzhash,32) != 0 ) - fprintf(stderr,"NTZ ERROR %s.%d %s != %s\n",coin,ntzheight,bits256_str(str,ntzhash),bits256_str(str2,prevntzhash)); - free_json(retjson); - } - } - } - else if ( argc == 5 && atol(hashstr) > 10000 ) - { - char checkstr[32]; uint64_t mult = 1; - M.origid = (uint32_t)atol(hashstr); - sprintf(checkstr,"%u",M.origid); - if ( strcmp(checkstr,hashstr) == 0 ) // alice - { - M.rel.satoshis = (uint64_t)(atof(argv[4])*SATOSHIDEN+0.0000000049999); - M.rel.satoshis = (M.rel.satoshis/BETDAPP_MAXPAYMENTS) * BETDAPP_MAXPAYMENTS; - for (i=0; M.rel.name[i]!=0; i++) - if ( M.rel.name[i] == '.' ) - { - mult = SATOSHIDEN; - break; - } - if ( subatomic_getbalance(&M.rel) < M.rel.satoshis/mult ) - { - fprintf(stderr,"not enough balance %s %.8f for %.8f\n",M.rel.coin,dstr(subatomic_getbalance(&M.rel)),dstr(M.rel.satoshis/mult)); - return(-1); - } - fprintf(stderr,"subatomic_channel_alice (%s/%s) %s %u with %.8f %llu\n",M.rel.name,M.rel.coin,hashstr,M.origid,atof(argv[4]),(long long)M.rel.satoshis); - dpow_pubkeyregister(SUBATOMIC_PRIORITY); - M.openrequestid = subatomic_alice_openrequest(&M); - if ( M.openrequestid != 0 ) - subatomic_loop(&M); - } else fprintf(stderr,"checkstr mismatch %s %s != %s\n",coin,hashstr,checkstr); - } - else - { - M.bobflag = 1; - strcpy(M.base.name,hashstr); - strcpy(M.base.coin,subatomic_checkname(tmpstr,&M,0,hashstr)); - subatomic_loop(&M); // while ( 1 ) loop for each relcoin -> basecoin - } - } - return(SUBATOMIC_retval); -} - diff --git a/src/cc/dapps/dappinc.h b/src/cc/dapps/dappinc.h deleted file mode 100644 index 58f82521aed..00000000000 --- a/src/cc/dapps/dappinc.h +++ /dev/null @@ -1,1679 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2020 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include -#include -#include -#include -#include "cJSON.c" -#include -#include "subatomic_utils.h" - -bits256 zeroid; - -int32_t unstringbits(char *buf,uint64_t bits) -{ - int32_t i; - for (i=0; i<8; i++,bits>>=8) - if ( (buf[i]= (char)(bits & 0xff)) == 0 ) - break; - buf[i] = 0; - return(i); -} - -uint64_t stringbits(char *str) -{ - uint64_t bits = 0; - if ( str == 0 ) - return(0); - int32_t i,n = (int32_t)strlen(str); - if ( n > 8 ) - n = 8; - for (i=n-1; i>=0; i--) - bits = (bits << 8) | (str[i] & 0xff); - //printf("(%s) -> %llx %llu\n",str,(long long)bits,(long long)bits); - return(bits); -} - -char hexbyte(int32_t c) -{ - c &= 0xf; - if ( c < 10 ) - return('0'+c); - else if ( c < 16 ) - return('a'+c-10); - else return(0); -} - -int32_t _unhex(char c) -{ - if ( c >= '0' && c <= '9' ) - return(c - '0'); - else if ( c >= 'a' && c <= 'f' ) - return(c - 'a' + 10); - else if ( c >= 'A' && c <= 'F' ) - return(c - 'A' + 10); - return(-1); -} - -int32_t is_hexstr(char *str,int32_t n) -{ - int32_t i; - if ( str == 0 || str[0] == 0 ) - return(0); - for (i=0; str[i]!=0; i++) - { - if ( n > 0 && i >= n ) - break; - if ( _unhex(str[i]) < 0 ) - break; - } - if ( n == 0 ) - return(i); - return(i == n); -} - -int32_t unhex(char c) -{ - int32_t hex; - if ( (hex= _unhex(c)) < 0 ) - { - //printf("unhex: illegal hexchar.(%c)\n",c); - } - return(hex); -} - -unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); } - -int32_t decode_hex(unsigned char *bytes,int32_t n,char *hex) -{ - int32_t adjust,i = 0; - //printf("decode.(%s)\n",hex); - if ( is_hexstr(hex,n) <= 0 ) - { - memset(bytes,0,n); - return(n); - } - if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) - hex[--n] = 0; - if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) - hex[--n] = 0; - if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) ) - { - if ( n > 0 ) - { - bytes[0] = unhex(hex[0]); - printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex)); - } - bytes++; - hex++; - adjust = 1; - } else adjust = 0; - if ( n > 0 ) - { - for (i=0; i>4) & 0xf); - hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf); - //printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]); - } - hexbytes[len*2] = 0; - //printf("len.%ld\n",len*2+1); - return((int32_t)len*2+1); -} - -long _stripwhite(char *buf,int accept) -{ - int32_t i,j,c; - if ( buf == 0 || buf[0] == 0 ) - return(0); - for (i=j=0; buf[i]!=0; i++) - { - buf[j] = c = buf[i]; - if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') ) - j++; - } - buf[j] = 0; - return(j); -} - -char *clonestr(char *str) -{ - char *clone; - if ( str == 0 || str[0]==0) - { - printf("warning cloning nullstr.%p\n",str); - //#ifdef __APPLE__ - // while ( 1 ) sleep(1); - //#endif - str = (char *)""; - } - clone = (char *)malloc(strlen(str)+16); - strcpy(clone,str); - return(clone); -} - -int32_t safecopy(char *dest,char *src,long len) -{ - int32_t i = -1; - if ( src != 0 && dest != 0 && src != dest ) - { - if ( dest != 0 ) - memset(dest,0,len); - for (i=0; i0; i--) - str[i] = str[i-1]; - str[0] = '/'; - str[n+1] = 0; - }*/ -#endif - return(str); -#endif -} - -void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) -{ - FILE *fp; - long filesize,buflen = *allocsizep; - uint8_t *buf = *bufp; - *lenp = 0; - if ( (fp= fopen(portable_path(fname),"rb")) != 0 ) - { - fseek(fp,0,SEEK_END); - filesize = ftell(fp); - if ( filesize == 0 ) - { - fclose(fp); - *lenp = 0; - //printf("loadfile null size.(%s)\n",fname); - return(0); - } - if ( filesize > buflen ) - { - *allocsizep = filesize; - *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); - } - rewind(fp); - if ( buf == 0 ) - printf("Null buf ???\n"); - else - { - if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) - printf("error reading filesize.%ld\n",(long)filesize); - buf[filesize] = 0; - } - fclose(fp); - *lenp = filesize; - //printf("loaded.(%s)\n",buf); - } //else printf("OS_loadfile couldnt load.(%s)\n",fname); - return(buf); -} - -void *filestr(long *allocsizep,char *_fname) -{ - long filesize = 0; char *fname,*buf = 0; void *retptr; - *allocsizep = 0; - fname = malloc(strlen(_fname)+1); - strcpy(fname,_fname); - retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); - free(fname); - return(retptr); -} - -char *send_curl(char *url,char *fname) -{ - long fsize; char curlstr[1024]; - sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); - system(curlstr); - return(filestr(&fsize,fname)); -} - -cJSON *get_urljson(char *url,char *fname) -{ - char *jsonstr; cJSON *json = 0; - if ( (jsonstr= send_curl(url,fname)) != 0 ) - { - //printf("(%s) -> (%s)\n",url,jsonstr); - json = cJSON_Parse(jsonstr); - free(jsonstr); - } - return(json); -} - -////////////////////////////////////////////// -// start of dapp -////////////////////////////////////////////// -int md_unlink(char *file) -{ -#ifdef _WIN32 - _chmod(file, 0600); - return( _unlink(file) ); -#else - return(unlink(file)); -#endif -} - -char *REFCOIN_CLI,DPOW_pubkeystr[67],DPOW_secpkeystr[67],DPOW_handle[67],DPOW_recvaddr[64],DPOW_recvZaddr[128]; - -cJSON *get_komodocli(char *refcoin,char **retstrp,char *acname,char *method,char *arg0,char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6) -{ - long fsize; cJSON *retjson = 0; char cmdstr[32768],*jsonstr,fname[32768]; - sprintf(fname,"/tmp/notarizer_%s_%d",method,(rand() >> 17) % 10000); - //if ( (acname == 0 || acname[0] == 0) && strcmp(refcoin,"KMD") != 0 ) - // acname = refcoin; - if ( acname[0] != 0 ) - { - if ( refcoin[0] != 0 && strcmp(refcoin,"KMD") != 0 && strcmp(refcoin,acname) != 0 ) - printf("unexpected: refcoin.(%s) acname.(%s)\n",refcoin,acname); - sprintf(cmdstr,"komodo-cli -ac_name=%s %s %s %s %s %s %s %s %s > %s\n",acname,method,arg0,arg1,arg2,arg3,arg4,arg5,arg6,fname); - } - else if ( strcmp(refcoin,"KMD") == 0 ) - sprintf(cmdstr,"komodo-cli %s %s %s %s %s %s %s %s > %s\n",method,arg0,arg1,arg2,arg3,arg4,arg5,arg6,fname); - else if ( REFCOIN_CLI != 0 && REFCOIN_CLI[0] != 0 ) - { - sprintf(cmdstr,"%s %s %s %s %s %s %s %s %s > %s\n",REFCOIN_CLI,method,arg0,arg1,arg2,arg3,arg4,arg5,arg6,fname); - //printf("ref.(%s) REFCOIN_CLI (%s)\n",refcoin,cmdstr); - } -//fprintf(stderr,"system(%s)\n",cmdstr); - system(cmdstr); - *retstrp = 0; - if ( (jsonstr= filestr(&fsize,fname)) != 0 ) - { - jsonstr[strlen(jsonstr)-1]='\0'; - //fprintf(stderr,"%s -> jsonstr.(%s)\n",cmdstr,jsonstr); - if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0 ) - *retstrp = jsonstr; - else free(jsonstr); - md_unlink(fname); - } //else fprintf(stderr,"system(%s) -> NULL\n",cmdstr); - return(retjson); -} - -// TODO @Milerius port subatomic_cli to use curl instead of system() for rpc interface call invocations -// task resources: -// https://curl.haxx.se/libcurl/c/post-callback.html -// https://en.bitcoin.it/wiki/API_reference_(JSON-RPC)#C -// fetch rpc auth data and rpc port from conf files - src: -// https://github.com/KomodoPlatform/komodo/blob/DEX/src/util.cpp -// https://github.com/KomodoPlatform/komodo/blob/DEX/src/komodo_utils.h -struct curl_string { - char *ptr; - size_t len; -}; - -void init_string(struct curl_string *s) { - s->len = 0; - s->ptr = malloc(s->len+1); - if (s->ptr == NULL) { - fprintf(stderr, "malloc() failed\n"); - exit(EXIT_FAILURE); - } - s->ptr[0] = '\0'; -} - -size_t writefunc(void *ptr, size_t size, size_t nmemb, struct curl_string *s) -{ - size_t new_len = s->len + size*nmemb; - s->ptr = realloc(s->ptr, new_len+1); - if (s->ptr == NULL) { - fprintf(stderr, "realloc() failed\n"); - exit(EXIT_FAILURE); - } - memcpy(s->ptr+s->len, ptr, size*nmemb); - s->ptr[new_len] = '\0'; - s->len = new_len; - return size*nmemb; -} - -cJSON *subatomic_cli(char *coin,char **retstrp,char *method,char *arg0,char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6) -{ - if (!is_komodo_config_filled()) { - fill_config(); - } - - CURL *curl = curl_easy_init(); - cJSON* retjson = NULL; - char* jsonstr = NULL; - struct curl_slist *headers = NULL; - struct curl_string s; - char* full_url = NULL; - //! Init curl string - init_string(&s); - - //! Headers - headers = curl_slist_append(headers, "Accept: application/json"); - headers = curl_slist_append(headers, "Content-Type: application/json"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - - - const char* port = get_rpcport(coin); - full_url = concatenate("http://127.0.0.1:", get_rpcport(coin)); - curl_easy_setopt(curl, CURLOPT_URL, full_url); - - //! credentials - const char* user = get_rpcuser(coin); - const char* password = get_rpcpassword(coin); - const char* tmp = concatenate(user, ":"); - const char* credentials = concatenate(tmp, password); - - printf("credentials: %s\n", credentials); - free(tmp); - curl_easy_setopt(curl, CURLOPT_USERPWD, credentials); - - //! Post contents and Size - jsonstr = construct_json(method, 7, arg0, arg1, arg2, arg3, arg4, arg5, arg6); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonstr); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(jsonstr)); - - //! Setting callback - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - - //! Perform - CURLcode res = curl_easy_perform(curl); - printf("%s\n", curl_easy_strerror(res)); - - printf("answer: %s\n", s.ptr); - - //! Copy answer to cJSON - retjson = cJSON_Parse(s.ptr); - - //! Release data - curl_easy_cleanup(curl); - curl_slist_free_all(headers); - free(full_url); - free(credentials); - free(jsonstr); - return(retjson); -} - -bits256 komodobroadcast(char *refcoin,char *acname,cJSON *hexjson) -{ - char *hexstr,*retstr,str[65]; cJSON *retjson; bits256 txid; - memset(txid.bytes,0,sizeof(txid)); - if ( (hexstr= jstr(hexjson,"hex")) != 0 ) - { - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"sendrawtransaction",hexstr,"","","","","","")) != 0 ) - { - //fprintf(stderr,"broadcast.(%s)\n",jprint(retjson,0)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - if ( strlen(retstr) >= 64 ) - { - retstr[64] = 0; - decode_hex(txid.bytes,32,retstr); - } - fprintf(stderr,"broadcast %s txid.(%s)\n",strlen(acname)>0?acname:refcoin,bits256_str(str,txid)); - free(retstr); - } - } - return(txid); -} - -bits256 sendtoaddress(char *refcoin,char *acname,char *destaddr,int64_t satoshis,char *oprethexstr) -{ - char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid; - memset(txid.bytes,0,sizeof(txid)); - sprintf(numstr,"%.8f",(double)satoshis/SATOSHIDEN); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"sendtoaddress",destaddr,numstr,"false","","",oprethexstr,"")) != 0 ) - { - fprintf(stderr,"unexpected sendrawtransaction json.(%s)\n",jprint(retjson,0)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - if ( strlen(retstr) >= 64 ) - { - retstr[64] = 0; - decode_hex(txid.bytes,32,retstr); - } - fprintf(stderr,"sendtoaddress %s %.8f txid.(%s)\n",destaddr,(double)satoshis/SATOSHIDEN,bits256_str(str,txid)); - free(retstr); - } - return(txid); -} - -bits256 tokentransfer(char *refcoin,char *acname,char *tokenid,char *destpub,int64_t units) -{ - char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid; - memset(txid.bytes,0,sizeof(txid)); - sprintf(numstr,"%llu",(long long)units); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"tokentransfer",tokenid,destpub,numstr,"","","","")) != 0 ) - { - txid = komodobroadcast(refcoin,acname,retjson); - fprintf(stderr,"tokentransfer returned (%s)\n",jprint(retjson,0)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"tokentransfer.(%s) error.(%s)\n",acname,retstr); - free(retstr); - } - return(txid); -} - -char *get_tokenaddress(char *refcoin,char *acname,char *tokenaddr) -{ - char *retstr,*str; cJSON *retjson; - tokenaddr[0] = 0; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"tokenaddress","","","","","","","")) != 0 ) - { - if ( (str= jstr(retjson,"myCCAddress(Tokens)")) != 0 ) - { - strcpy(tokenaddr,str); - fprintf(stderr,"tokenaddress returned (%s)\n",tokenaddr); - free_json(retjson); - return(tokenaddr); - } - free_json(retjson); - } - else if ( retstr != 0 ) - { - //fprintf(stderr,"tokentransfer.(%s) error.(%s)\n",acname,retstr); - free(retstr); - } - return(0); -} - -int64_t get_tokenbalance(char *refcoin,char *acname,char *tokenid) -{ - cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"tokenbalance",tokenid,"","","","","","")) != 0 ) - { - amount = j64bits(retjson,"balance"); - fprintf(stderr,"tokenbalance %llu\n",(long long)amount); - free_json(retjson); - } - else if ( retstr != 0 ) - { - //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); - free(retstr); - } - return (amount); -} - -cJSON *get_decodescript(char *refcoin,char *acname,char *script) -{ - cJSON *retjson; char *retstr; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"decodescript",script,"","","","","","")) != 0 ) - { - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"get_decodescript.(%s) error.(%s)\n",acname,retstr); - free(retstr); - } - return(0); -} - -char *get_createmultisig2(char *refcoin,char *acname,char *msigaddr,char *redeemscript,char *pubkeyA,char *pubkeyB) -{ - //char para 2 '["02c3af47b51a506b08b4ededb156cb4c3f9db9e0ac7ad27b8623c08a056fdcc220", "038e61fbface549a850862f12ed99b7cbeef5c2bd2d8f1daddb34809416f0259e1"]' - cJSON *retjson; char *retstr,*str,params[256]; int32_t height=0; - msigaddr[0] = 0; - redeemscript[0] = 0; - sprintf(params,"'[\"%s\", \"%s\"]'",pubkeyA,pubkeyB); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"createmultisig","2",params,"","","","","")) != 0 ) - { - if ( (str= jstr(retjson,"address")) != 0 ) - strcpy(msigaddr,str); - if ( (str= jstr(retjson,"redeemScript")) != 0 ) - strcpy(redeemscript,str); - free_json(retjson); - if ( msigaddr[0] != 0 && redeemscript[0] != 0 ) - return(msigaddr); - else return(0); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"%s get_createmultisig2.(%s) error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(0); -} - -int32_t get_coinheight(char *refcoin,char *acname,bits256 *blockhashp) -{ - cJSON *retjson; char *retstr; int32_t height=0; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockchaininfo","","","","","","","")) != 0 ) - { - height = jint(retjson,"blocks"); - *blockhashp = jbits256(retjson,"bestblockhash"); - free_json(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"%s get_coinheight.(%s) error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(height); -} - -bits256 get_coinblockhash(char *refcoin,char *acname,int32_t height) -{ - cJSON *retjson; char *retstr,heightstr[32]; bits256 hash; - memset(hash.bytes,0,sizeof(hash)); - sprintf(heightstr,"%d",height); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockhash",heightstr,"","","","","","")) != 0 ) - { - fprintf(stderr,"unexpected blockhash json.(%s)\n",jprint(retjson,0)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - if ( strlen(retstr) >= 64 ) - { - retstr[64] = 0; - decode_hex(hash.bytes,32,retstr); - } - free(retstr); - } - return(hash); -} - -bits256 get_coinmerkleroot(char *refcoin,char *acname,bits256 blockhash,uint32_t *blocktimep) -{ - cJSON *retjson; char *retstr,str[65]; bits256 merkleroot; - memset(merkleroot.bytes,0,sizeof(merkleroot)); - *blocktimep = 0; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockheader",bits256_str(str,blockhash),"","","","","","")) != 0 ) - { - merkleroot = jbits256(retjson,"merkleroot"); - *blocktimep = juint(retjson,"time"); - //fprintf(stderr,"got merkleroot.(%s)\n",bits256_str(str,merkleroot)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"%s %s get_coinmerkleroot error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(merkleroot); -} - -uint32_t get_heighttime(char *refcoin,char *acname,int32_t height) -{ - bits256 blockhash; uint32_t blocktime; - blockhash = get_coinblockhash(refcoin,acname,height); - get_coinmerkleroot(refcoin,acname,blockhash,&blocktime); - return(blocktime); -} - -int32_t get_coinheader(char *refcoin,char *acname,bits256 *blockhashp,bits256 *merklerootp,int32_t prevheight) -{ - int32_t height = 0; char str[65]; bits256 bhash; uint32_t blocktime; - if ( prevheight == 0 ) - height = get_coinheight(refcoin,acname,&bhash) - 20; - else height = prevheight + 1; - if ( height > 0 ) - { - *blockhashp = get_coinblockhash(refcoin,acname,height); - if ( bits256_nonz(*blockhashp) != 0 ) - { - *merklerootp = get_coinmerkleroot(refcoin,acname,*blockhashp,&blocktime); - if ( bits256_nonz(*merklerootp) != 0 ) - return(height); - } - } - memset(blockhashp,0,sizeof(*blockhashp)); - memset(merklerootp,0,sizeof(*merklerootp)); - return(0); -} - -cJSON *get_rawmempool(char *refcoin,char *acname) -{ - cJSON *retjson; char *retstr; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getrawmempool","","","","","","","")) != 0 ) - { - //printf("mempool.(%s)\n",jprint(retjson,0)); - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"get_rawmempool.(%s) error.(%s)\n",acname,retstr); - free(retstr); - } - return(0); -} - -cJSON *get_addressutxos(char *refcoin,char *acname,char *coinaddr) -{ - cJSON *retjson; char *retstr,jsonbuf[256]; - if ( refcoin[0] != 0 && strcmp(refcoin,"KMD") != 0 ) - printf("warning: assumes %s has addressindex enabled\n",refcoin); - sprintf(jsonbuf,"{\\\"addresses\\\":[\\\"%s\\\"]}",coinaddr); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getaddressutxos",jsonbuf,"","","","","","")) != 0 ) - { - //printf("addressutxos.(%s)\n",jprint(retjson,0)); - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"get_addressutxos.(%s) error.(%s)\n",acname,retstr); - free(retstr); - } - return(0); -} - -cJSON *get_rawtransaction(char *refcoin,char *acname,bits256 txid) -{ - cJSON *retjson; char *retstr,str[65]; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getrawtransaction",bits256_str(str,txid),"1","","","","","")) != 0 ) - { - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"get_rawtransaction.(%s) %s error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(0); -} - -cJSON *get_z_viewtransaction(char *refcoin,char *acname,bits256 txid) -{ - cJSON *retjson; char *retstr,str[65]; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_viewtransaction",bits256_str(str,txid),"","","","","","")) != 0 ) - { - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"get_z_viewtransaction.(%s) %s error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(0); -} - -cJSON *get_listunspent(char *refcoin,char *acname) -{ - cJSON *retjson; char *retstr,str[65]; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"listunspent","","","","","","","")) != 0 ) - { - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"get_listunspent.(%s) %s error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(0); -} - -cJSON *get_getinfo(char *refcoin,char *acname) -{ - cJSON *retjson; char *retstr,str[65]; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getinfo","","","","","","","")) != 0 ) - { - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"get_getinfo.(%s) %s error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(0); -} - -cJSON *z_listunspent(char *refcoin,char *acname) -{ - cJSON *retjson; char *retstr,str[65]; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_listunspent","","","","","","","")) != 0 ) - { - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"z_listunspent.(%s) %s error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(0); -} - -cJSON *z_listoperationids(char *refcoin,char *acname) -{ - cJSON *retjson; char *retstr,str[65]; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_listoperationids","","","","","","","")) != 0 ) - { - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"z_listoperationids.(%s) %s error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(0); -} - -cJSON *z_getoperationstatus(char *refcoin,char *acname,char *opid) -{ - cJSON *retjson; char *retstr,str[65],params[512]; - sprintf(params,"'[\"%s\"]'",opid); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_getoperationstatus",params,"","","","","","")) != 0 ) - { - //printf("got status (%s)\n",jprint(retjson,0)); - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"z_getoperationstatus.(%s) %s error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(0); -} - -cJSON *z_getoperationresult(char *refcoin,char *acname,char *opid) -{ - cJSON *retjson; char *retstr,str[65],params[512]; - sprintf(params,"'[\"%s\"]'",opid); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_getoperationresult",params,"","","","","","")) != 0 ) - { - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"z_getoperationresult.(%s) %s error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(0); -} - -int32_t validateaddress(char *refcoin,char *acname,char *depositaddr, char* compare) -{ - cJSON *retjson; char *retstr; int32_t res=0; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"validateaddress",depositaddr,"","","","","","")) != 0 ) - { - if (is_cJSON_True(jobj(retjson,compare)) != 0 ) res=1; - free_json(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return (res); -} - -int32_t z_validateaddress(char *refcoin,char *acname,char *depositaddr, char *compare) -{ - cJSON *retjson; char *retstr; int32_t res=0; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_validateaddress",depositaddr,"","","","","","")) != 0 ) - { - if (is_cJSON_True(jobj(retjson,compare)) != 0 ) - res=1; - free_json(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"z_validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return (res); -} - -int64_t get_getbalance(char *refcoin,char *acname) -{ - cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getbalance","","","","","","","")) != 0 ) - { - fprintf(stderr,"get_getbalance.(%s) %s returned json!\n",refcoin,acname); - free_json(retjson); - } - else if ( retstr != 0 ) - { - amount = atof(retstr) * SATOSHIDEN; - sprintf(cmpstr,"%.8f",dstr(amount)); - if ( strcmp(retstr,cmpstr) != 0 ) - amount++; - //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); - free(retstr); - } - return (amount); -} - -int64_t z_getbalance(char *refcoin,char *acname,char *coinaddr) -{ - cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_getbalance",coinaddr,"","","","","","")) != 0 ) - { - fprintf(stderr,"z_getbalance.(%s) %s returned json!\n",refcoin,acname); - free_json(retjson); - } - else if ( retstr != 0 ) - { - amount = atof(retstr) * SATOSHIDEN; - sprintf(cmpstr,"%.8f",dstr(amount)); - if ( strcmp(retstr,cmpstr) != 0 ) - amount++; - //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); - free(retstr); - } - return (amount); -} - -int32_t z_exportkey(char *privkey,char *refcoin,char *acname,char *zaddr) -{ - cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; - privkey[0] = 0; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_exportkey",zaddr,"","","","","","")) != 0 ) - { - fprintf(stderr,"z_exportkey.(%s) %s returned json!\n",refcoin,acname); - free_json(retjson); - return(-1); - } - else if ( retstr != 0 ) - { - //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); - strcpy(privkey,retstr); - free(retstr); - return(0); - } - return(-1); -} - -int32_t getnewaddress(char *coinaddr,char *refcoin,char *acname) -{ - cJSON *retjson; char *retstr; int64_t amount=0; int32_t retval = -1; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getnewaddress","","","","","","","")) != 0 ) - { - fprintf(stderr,"getnewaddress.(%s) %s returned json!\n",refcoin,acname); - free_json(retjson); - } - else if ( retstr != 0 ) - { - strcpy(coinaddr,retstr); - free(retstr); - retval = 0; - } - return(retval); -} - -int32_t z_getnewaddress(char *coinaddr,char *refcoin,char *acname,char *typestr) -{ - cJSON *retjson; char *retstr; int64_t amount=0; int32_t retval = -1; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_getnewaddress",typestr,"","","","","","")) != 0 ) - { - fprintf(stderr,"z_getnewaddress.(%s) %s returned json!\n",refcoin,acname); - free_json(retjson); - } - else if ( retstr != 0 ) - { - strcpy(coinaddr,retstr); - free(retstr); - retval = 0; - } - return(retval); -} - -int64_t find_onetime_amount(char *coinstr,char *coinaddr) -{ - cJSON *array,*item; int32_t i,n; char *addr; int64_t amount = 0; - coinaddr[0] = 0; - if ( (array= get_listunspent(coinstr,"")) != 0 ) - { - //printf("got listunspent.(%s)\n",jprint(array,0)); - if ( (n= cJSON_GetArraySize(array)) > 0 ) - { - for (i=0; i 0 ) - { - for (i=0; i %s\n",coinstr,acname,srcaddr,params); - if ( (retjson= get_komodocli(coinstr,&retstr,acname,"z_sendmany",addr,params,"","","","","")) != 0 ) - { - printf("unexpected json z_sendmany.(%s)\n",jprint(retjson,0)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"z_sendmany.(%s) -> opid.(%s)\n",coinstr,retstr); - strcpy(opidstr,retstr); - free(retstr); - retval = 0; - } - return(retval); -} - -int32_t z_mergetoaddress(char *opidstr,char *coinstr,char *acname,char *destaddr) -{ - cJSON *retjson; char *retstr,addr[128],*opstr; int32_t retval = -1; - sprintf(addr,"[\\\"ANY_SPROUT\\\"]"); - if ( (retjson= get_komodocli(coinstr,&retstr,acname,"z_mergetoaddress",addr,destaddr,"","","","","")) != 0 ) - { - if ( (opstr= jstr(retjson,"opid")) != 0 ) - strcpy(opidstr,opstr); - retval = jint(retjson,"remainingNotes"); - fprintf(stderr,"%s\n",jprint(retjson,0)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"z_mergetoaddress.(%s) -> opid.(%s)\n",coinstr,retstr); - strcpy(opidstr,retstr); - free(retstr); - } - return(retval); -} - -int32_t empty_mempool(char *coinstr,char *acname) -{ - cJSON *array; int32_t n; - if ( (array= get_rawmempool(coinstr,acname)) != 0 ) - { - if ( (n= cJSON_GetArraySize(array)) > 0 ) - return(0); - free_json(array); - return(1); - } - return(-1); -} - -cJSON *getinputarray(int64_t *totalp,cJSON *unspents,int64_t required) -{ - cJSON *vin,*item,*vins = cJSON_CreateArray(); int32_t i,n,v; int64_t satoshis; bits256 txid; - *totalp = 0; - if ( (n= cJSON_GetArraySize(unspents)) > 0 ) - { - for (i=0; i= required ) - break; - } - } - } - return(vins); -} - -int32_t tx_has_voutaddress(char *refcoin,char *acname,bits256 txid,char *coinaddr) -{ - cJSON *txobj,*vouts,*vout,*vins,*vin,*sobj,*addresses; char *addr,str[65]; int32_t i,j,n,numarray,retval = 0, hasvout=0; - if ( (txobj= get_rawtransaction(refcoin,acname,txid)) != 0 ) - { - if ( (vouts= jarray(&numarray,txobj,"vout")) != 0 ) - { - for (i=0; i 0 ) - { - for (i=0; i 0 ) - { - for (j=0; j 0 && strcmp(vinaddr,cmpaddr) == 0 ) - return(0); - printf("mismatched vinaddr.(%s) vs %s\n",vinaddr,cmpaddr); - } - } - return(-1); -} - -int32_t txid_in_vins(char *refcoin,bits256 txid,bits256 cmptxid) -{ - cJSON *txjson,*vins,*vin; int32_t numvins,v,vinvout; bits256 vintxid; char str[65]; - if ( (txjson= get_rawtransaction(refcoin,"",txid)) != 0 ) - { - if ( (vins= jarray(&numvins,txjson,"vin")) != 0 ) - { - for (v=0; v n.%d retval.%d\n",tagA,tagB,pubkeystr,n,retval); - } - free_json(retjson); - } - return(retval); -} - -int32_t dpow_hasmessage(char *payload,char *tagA,char *tagB,char *pubkeystr) -{ - cJSON *retjson,*item,*array; char *retstr,*pstr; int32_t i,n,retval = 0; - if ( (retjson= get_komodocli((char *)"",&retstr,DEXP2P_CHAIN,"DEX_list","0","0",tagA,tagB,pubkeystr,"","")) != 0 ) - { - if ( (array= jarray(&n,retjson,"matches")) != 0 ) - { - for (i=0; i 0 ) - { - ptrs = calloc(n,sizeof(*ptrs)); - for (i=0; ishorthash = juint(item,"id"); - ptrs[m]->jsonstr = ptr; - strcpy(ptrs[m]->senderpub,senderpub); - m++; - } - } - } - *nump = m; - } - free_json(retjson); - } - return(ptrs); -} - - diff --git a/src/cc/dapps/make_betdapp.sh b/src/cc/dapps/make_betdapp.sh deleted file mode 100755 index 8a2826aeee1..00000000000 --- a/src/cc/dapps/make_betdapp.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -echo "Building betdapp" -g++-8 -c -std=c++17 -Wfatal-errors -Wall -Wextra -Werror subatomic_utils.cpp -gcc-8 -Wall -Wextra -Wfatal-errors -o ../../betdapp betdapp.c subatomic_utils.o -lstdc++ -lstdc++fs -lcurl -lm -echo "Build finished" \ No newline at end of file diff --git a/src/cc/dapps/make_notarizer.sh b/src/cc/dapps/make_notarizer.sh deleted file mode 100755 index 72783713e0a..00000000000 --- a/src/cc/dapps/make_notarizer.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -echo "Building notarizer" -mkdir -p ~/.dpow/bin -g++-8 -c -std=c++17 -Wfatal-errors -Wall -Wextra -Werror subatomic_utils.cpp -gcc-8 -Wall -Wextra -Wfatal-errors -o ~/.dpow/bin/notarizer notarizer.c subatomic_utils.o -lstdc++ -lstdc++fs -lcurl -lm -echo "export PATH=$PATH:~/.dpow/bin/" >> ~/.bashrc # new session required to become effective -# run "export PATH=$PATH:~/.dpow/bin/" for immediate effect -echo "Build finished" \ No newline at end of file diff --git a/src/cc/dapps/make_subatomic.sh b/src/cc/dapps/make_subatomic.sh deleted file mode 100755 index 22f5ac97e74..00000000000 --- a/src/cc/dapps/make_subatomic.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -g++-8 -c -std=c++17 -Wfatal-errors -Wall -Wextra -Werror subatomic_utils.cpp -gcc-8 -Wall -Wextra -Wfatal-errors -o ../../subatomic subatomic.c subatomic_utils.o -lstdc++ -lstdc++fs -lcurl -lm diff --git a/src/cc/dapps/notarizer.c b/src/cc/dapps/notarizer.c deleted file mode 100644 index c753111e763..00000000000 --- a/src/cc/dapps/notarizer.c +++ /dev/null @@ -1,426 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2020 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#define DEXP2P_CHAIN ((char *)"DPOW") -#define DEXP2P_PUBKEYS ((char *)"pubkeys") - -// todo -// external coins, including BTC -// posting 13 signers (by first on list) and signature rounds (and prevent too close notarizations) -// load handling - -#include "dappinc.h" - -#define NOTARIZATION_TIME 300 // minimum every 5 minutes -#define _NOTARIZATION_BLOCKS 10 - -struct dpowentry { bits256 ntzhash; uint32_t timestamp; int32_t height,ind; char payload[81]; } NN[64]; - -int32_t Num_authorized,NOTARIZATION_BLOCKS = _NOTARIZATION_BLOCKS; -cJSON *NOTARIZER_json; -char *Authorized[64][3]; - -uint64_t Rvals[0x100] = -{ - 0xb4f898f421692acc, 0x2b2c0c298ef91317, 0x7108a286fc7fea53, 0xa73efcda0be414aa, 0x58d0c1401bc8a727, 0x282a13d5fee6c9d5, 0xf6f62e4c8425aafd, 0xfec440d71d02da42, 0x2380ea4e281a8ce8, 0x79b24b7b892167b4, 0x4939f30642fdd8f5, 0x64a32b7ca5673bcd, 0x5c2a0da8078cbdb8, 0x8d699897efe54b74, 0x27c1095a77cf6670, 0xd257054c0cc2844, 0x34323ebfd614b22d, 0x21af8cb02123188a, 0xcd7559884d987ff3, 0xe0e3b3b2d68e4281, 0xc058e83624b6f765, 0xfdd29109b1e17c00, 0x4665c0c4984a6b10, 0xd9600a7b51df403, 0x37ee8b362e324df8, 0x710fbb6111e8d008, 0x1d32701aeb0a5a26, 0x48196a4f0cd34f64, 0xdf2665d7ee764cb7, 0xf10900ca97f10e36, 0xe9930e8475dc145b, 0x89f60fd1aef5675e, 0x9129ed233786d5b, 0x5f69aa0477a103ad, 0xb32d695444d545bf, 0x8f2c5733a26123d0, 0xf0a1d44397c2a4c5, 0xac3cd1eddd6842d8, 0x4c1f1e7b6cf01217, 0x9d041bd875ee8041, 0x73f4d69a0335c6c0, 0x45b18d2e05505c18, 0x267d0c01bb1a29f, 0x31c67b7129775651, 0xea5029d2dc35fb41, 0xcef9a12eb4f0a185, 0xcc07507b05e9ab44, 0x3d4abcfb18f67fa7, 0x2f746aa98a1a7fb8, 0x4b08dc063ae3640b, 0xe9d058420350f187, 0x3b104d83d302c6d8, 0xa1a53a9575c52acc, 0xecf1e364a6db889f, 0x7202c19a18f042d5, 0x1e4656510a460487, 0xe5b8394617940b81, 0x4d137d77268b93c1, 0x4ccd67c55a6504bf, 0xebdc9e6abe57136c, 0x6cd5bbaaf00271a9, 0x9d13a6dec692614e, 0x4dbcc5e06ff879aa, 0x5c2ee8b2420b14d8, 0x3b7022ec044c3159, 0x540014aa62010383, 0x95bc80bd6ec4f88d, 0xaa307b047d8c795, 0x96607f12250eef38, 0x482f8772df86283, 0x5d442fc7ae7347f1, 0x5db14724a7a41df7, 0xc3e0e43f4a83c037, 0xc2c273d1bdf1d846, 0x5cdfb665827135bb, 0x456b5ccf80484d4, 0x5792ebeb54eda76, 0x999b85ded8c36a07, 0x977dfcc3211c0dbb, 0xae51e7374d908280, 0xd486ddcd80639fc1, 0x8abf48ce26ad4947, 0x59b129d819abdc56, 0xeb91f729e3a642ab, 0x1a3af2d3656cf597, 0x869401300a41e73b, 0x160f516ab6a040dd, 0x45a98caebdfab694, 0x4270e40755aa1582, 0x9083e1b3fc4cb1cb, 0x9a356758d462322, 0xad00702abbc79e0d, 0x5bcf728f8c02aa85, 0x2068dafed7571c24, 0x137c510573adaffe, 0x39c7ceb31ace43ef, 0x6164953c08c6c979, 0x94d0a196af39bbb1, 0xbf6984cf56077e50, 0x80b5a0067cd938c8, 0xc309ed11a7897b6a, 0x35d443e4407342a9, 0x2950f560918cdcc2, 0x35b55acb35a52a2e, 0xd6abe62ce7dd3fd6, 0x2442eb620c161e28, 0xf25107c428b6cec7, 0x2732dc51fc27f631, 0x1f1fbaf6480f101b, 0xe8616e203c2c35d9, 0x51f2611f0010173c, 0x12d180f6aa4e1958, 0x76a67a9c875ae191, 0x7eb81341d0b2d3af, 0x839b712a4a7ec82b, 0xfb93ff6a812de98a, 0x18f32a687c83eee0, 0xe5bda0b93fe8a5fd, 0x3717ccac93303b69, 0x241c4c4320b844b6, 0xc8bb94caa3a0a03a, 0xaed8c544f307a439, 0x8ae559d4ad420800, 0x2505c8eae1ab9e9d, 0x74fb853a33c9a668, 0xc91c40c86e670329, 0x56a85cb81d775f48, 0x850b3d1ce53754fb, 0x8492b45a1eb8d5e3, 0x82490f3a80f2f9b7, 0x187d481d27a0c06e, 0xd73e0af3b9feb59d, 0x62be87ab3af5f7df, 0x2f3e2cc1bbe3b181, 0xc522d4280a57dfed, 0xb55bd2b7e7e218a, 0x54c204d25f454ab4, 0xbfa64ed27610a5b5, 0x5d1becd256c9fd2e, 0xc44305839db9a0e, 0x44a12f8e4d6c8a7b, 0x2e676100a7bab1d5, 0x2c8da01e3084315e, 0x14a0aa999365693b, 0x1208033cc3620c35, 0x82e32123b5bac2b5, 0x449d400971b26852, 0xe35878ae43d803aa, 0x73d3afbec1a7ba85, 0x2180daf96298d72, 0xb6c4110c7f73cb75, 0x99a65214c2fa9c14, 0x703f2efdcb5a4e0d, 0xeb6e1f756b0668bc, 0x7ad2ff2f66be1ea, 0x77970f9dfcaa7cb, 0xbf2d1202bdd2d4bf, 0x3ddb9953f02b347a, 0x4175e1756e34467b, 0x708999b182204089, 0xe9305a6e702d5c6e, 0x7020b5f4e2d95b8e, 0xbfb2543671de54fb, 0xdbb5cc6d4542d394, 0xcc817c3d984ce329, 0xb3dce10ccae5ad7, 0x4192f705a7d1b23, 0x524c0029704a5c03, 0xedee6b1521ea98e3, 0x8fe2f76cd6b99dc6, 0x9387048f82ea5f12, 0xb5db50d28904d160, 0xf8ecc532fed6c6e9, 0xaf5eb29dcecfa662, 0xad34ba4ca1324811, 0x731312895e36388c, 0x12035eaf0b71ead8, 0x97513e85a1e0d304, 0xca816e3ed3398387, 0xa3a33cca3028b7a6, 0x77e8b273d6ae1526, 0x8fa36a080b18c985, 0xf1f6e44a2c63dced, 0x810ac55a66221e9c, 0xf0c6406a4e58b8db, 0x37a19f4911fbdf09, 0x85b859e9c663047c, 0x7c55f6a0d3ecdb78, 0xf70991ee19caadae, 0x21f1aba5b9090570, 0x271fa0ae99e854af, 0xb19851021cfb0b7b, 0x65e4d4f5ecca63fe, 0x48a010997f1feeda, 0xa53b012914e28865, 0x86c05b880fc2370d, 0x3a19575fb4738bbf, 0xaf275949deb93945, 0x18b1fe0b0cc30ae2, 0x8e44e8cfbe287336, 0x550e5c0fd442b874, 0x48e5c21df34c795, 0x878ac15171105266, 0xf938a9b0445b235, 0xc7a8f13252e3d542, 0x9fe03639dba7ddb9, 0xe8619cb2d911f088, 0x74f98031eaeda64e, 0x88506fce88c6dd56, 0xb625442d8564f74c, 0x22cff1d4e7903734, 0x933d063dc90dbc98, 0xc03b6c73b21c04e3, 0x19486be491459f63, 0x639acfca7700d8a3, 0x915d6aadc92ca78c, 0x49a37fe5e344796f, 0xf71c245403dbe81f, 0x782a09ecc76f1cfd, 0x31f0a0c25e4257b1, 0xd249b7b3ff143419, 0x46edf249f5d625a0, 0x17743966fb0e5c0e, 0x718466575376b690, 0xf121b9504e70999b, 0xf86ed3ea28e6f7de, 0x47ae68b696f47d30, 0x26e71f8a769a241f, 0xf0fa9612097458b8, 0xe67187b57819e18e, 0xec022acdbb7c6504, 0x74004abbda639c4f, 0x9aa5770bb47d75a2, 0x4bf583460e12be59, 0xeba3099cb60771e8, 0xf3d5c55061c506a6, 0x24e04ecb46a8533a, 0x5fa028765433f30d, 0x5722b9b03716a79a, 0xe9048eae10b8e8bf, 0x8d110fbad30ade2, 0xc8dafc2e7a5c0503, 0x69ac22a955fff0a4, 0xd6a37ef3b97a650b, 0xe72f55e32783d32b, 0x2d513c8988628c5a, 0x7e402e73d782512d, 0x665ddb3a8738bb93, 0x917abd36928de1ae, 0x46016b19d31adc4a, 0x46af5eb2301e84bc, 0x72bb2cc354cee71a, 0x50d57f064404480d, 0x18bc255b7623ef5c, 0x54a3f8395c49daa9, 0xac15649ea3a871df -}; - -// issue ./komodod -ac_name=DPOW -handle=xxx -dexp2p=2 -addnode=136.243.58.134 -pubkey=02/03... & -// add blocknotify=notarizer KMD "" %s -// add blocknotify=notarizer ASSETCHAIN "" %s -// add blocknotify=notarizer BTC "bitcoin-cli" %s -// add blocknotify=notarizer 3rdparty "3rdparty-cli" %s -// build notarizer and put in path: gcc cc/dapps/notarizer.c -lm -o notarizer; cp notarizer /usr/bin - -int32_t dpow_hashind(char *coin,char *handle,int32_t ntzheight) -{ - uint8_t hbuf[128]; uint64_t rval=0; int32_t i,x,n=0; - memset(hbuf,0,sizeof(hbuf)); - for (i=0; coin[i]!=0; i++) - hbuf[n++] = coin[i]; - hbuf[n++] = 0; - for (i=0; handle[i]!=0; i++) - hbuf[n++] = handle[i]; - hbuf[n++] = 0; - x = ntzheight; - for (i=0; x!=0; i++,x>>=1) - if ( (x & 1) != 0 ) - hbuf[n++] = (ntzheight + i) % 97; - hbuf[n++] = i; - for (i=0; i nn_b[0] ) - return(-1); - else if ( nn_a[0] < nn_b[0] ) - return(1); - else - { - if ( nn_a[1] > nn_b[1] ) - return(-1); - else if ( nn_a[1] < nn_b[1] ) - return(1); - } - return(0); -#undef nn_a -#undef nn_b -} - -static int _NN_sortcmp(const void *a,const void *b) -{ -#define nn_a ((struct dpowentry *)a) -#define nn_b ((struct dpowentry *)b) - int32_t i; - if ( nn_a->height > nn_b->height ) - return(-1); - else if ( nn_a->height < nn_b->height ) - return(1); - else - { - if ( nn_a->timestamp > nn_b->timestamp ) - return(-1); - else if ( nn_a->timestamp < nn_b->timestamp ) - return(1); - else - { - for (i=0; i<4; i++) - { - if ( nn_a->ntzhash.ulongs[i] > nn_b->ntzhash.ulongs[i] ) - return(-1); - else if ( nn_a->ntzhash.ulongs[i] < nn_b->ntzhash.ulongs[i] ) - return(1); - } - } - } - return(0); -#undef nn_a -#undef nn_b -} - -int32_t dpow_roundproposal(char *coin) -{ - uint8_t buf[4]; int32_t i,n,ntzheight,cmpind=-1,match0=0,matchB=0,cmpB=-1; uint64_t candidates[64][2]; char str[65],payload[65]; - for (i=n=0; i 0 ) - { - decode_hex(NN[n].ntzhash.bytes,32,NN[n].payload); - decode_hex(buf,4,NN[n].payload + 32*2); - NN[n].height = ((int32_t)buf[3] + ((int32_t)buf[2] << 8) + ((int32_t)buf[1] << 16) + ((int32_t)buf[0] << 24)); - decode_hex(buf,4,NN[n].payload + 32*2+8); - NN[n].timestamp = ((int32_t)buf[3] + ((int32_t)buf[2] << 8) + ((int32_t)buf[1] << 16) + ((int32_t)buf[0] << 24)); - //fprintf(stderr,"%s.%d t.%u %s\n",coin,NN[n].height,NN[n].timestamp,Authorized[i][0]); - n++; - } - } - if ( n >= 13 ) - { - qsort(NN,n,sizeof(NN[n]),_NN_sortcmp); - for (i=0; i 0 ) - { - if ( strcmp(NN[i].payload,NN[0].payload) == 0 ) - match0++; - else if ( cmpB < 0 ) - cmpB = i; - if ( cmpB >= 0 && i != cmpB && strcmp(NN[i].payload,NN[cmpB].payload) == 0 ) - matchB++; - } - } - fprintf(stderr,"%s num.%d match0.%d cmpB.%d matchB.%d\n",coin,n,match0,cmpB,matchB); - if ( matchB > match0 && matchB > 33 ) - cmpind = cmpB; - else if ( match0 > 33 ) - cmpind = 0; - if ( cmpind >= 0 ) // search for all registered notaries with matching hash - { - ntzheight = NN[cmpind].height; - sprintf(str,"%u",ntzheight); - for (i=n=0; i 0 ) - { - if ( strncmp(payload,NN[cmpind].payload,strlen(payload)) == 0 ) - { - candidates[n][1] = i; - candidates[n][0] = dpow_hashind(coin,Authorized[i][0],ntzheight); - n++; - } - } - } - qsort(candidates,n,sizeof(candidates[n]),_candidates_sortcmp); - for (i=0; i<13; i++) - fprintf(stderr,"%s ",Authorized[candidates[i][1]][0]); - fprintf(stderr,"h.%d t.%u %s %s signers\n",NN[i].height,NN[i].timestamp,bits256_str(str,NN[i].ntzhash),coin); - return(0); - } - } else fprintf(stderr,"%s only has num.%d\n",coin,n); - return(-1); -} - -void dpow_hashind_test(int32_t *histo,char *coin,int32_t height) -{ - int32_t i,n; uint64_t candidates[64][2]; - memset(candidates,0,sizeof(candidates)); - for (i=n=0; i maxh ) - maxh = histo[z]; - } - fprintf(stderr,"minh.%d vs maxh.%d\n",minh,maxh); - } - prevntzhash = dpow_ntzhash(coin,&prevntzheight,&prevntztime); - if ( (retjson= get_getinfo(coin,acname)) != 0 ) - { - ntzheight = juint(retjson,"notarized"); - ntzhash = jbits256(retjson,"notarizedhash"); - if ( ntzheight > prevntzheight ) - { - get_coinmerkleroot(coin,acname,ntzhash,&ntztime); - fprintf(stderr,"NOTARIZATION %s.%d %s t.%u\n",coin,ntzheight,bits256_str(str,ntzhash),ntztime); - bits256_str(hexstr,ntzhash); - sprintf(&hexstr[64],"%08x",ntzheight); - sprintf(&hexstr[72],"%08x",ntztime); - hexstr[80] = 0; - if ( (retjson2= dpow_broadcast(priority,hexstr,coin,"notarizations",DPOW_pubkeystr,"","")) != 0 ) - free_json(retjson2); - } - else if ( ntzheight == prevntzheight ) - { - if ( memcmp(&prevntzhash,&ntzhash,32) != 0 ) - fprintf(stderr,"NTZ ERROR %s.%d %s != %s\n",coin,ntzheight,bits256_str(str,ntzhash),bits256_str(str2,prevntzhash)); - else if ( height > ntzheight ) - { - for (h=height; h>ntzheight && h>height-100; h--) - { - checkhash = dpow_blockhash(coin,h); // from network - chainhash = get_coinblockhash(coin,acname,h); // from blockchain - if ( memcmp(&checkhash,&chainhash,sizeof(checkhash)) != 0 ) - { - fprintf(stderr,"%s.%d: chainhash.%s != %s, must have been reorged\n",coin,h,bits256_str(str,chainhash),bits256_str(str2,checkhash)); - if ( (retjson2= dpow_ntzdata(coin,priority,h,chainhash)) != 0 ) - free_json(retjson2); - } - } - } - } - free_json(retjson); - if ( strcmp("KMD",coin) != 0 ) - NOTARIZATION_BLOCKS = 1; - nextheight = ntzheight + NOTARIZATION_BLOCKS - (ntzheight % NOTARIZATION_BLOCKS); - if ( nextheight < height - NOTARIZATION_BLOCKS/2 ) - { - nexttime = get_heighttime(coin,acname,nextheight); - if ( (time(NULL) - nexttime) > 2*NOTARIZATION_TIME ) // find a more recent block - { - for (i=NOTARIZATION_BLOCKS; nextheight+i < height-NOTARIZATION_BLOCKS/2 - 1; i+=NOTARIZATION_BLOCKS) - { - t = get_heighttime(coin,acname,nextheight+i); - if ( 0 && NOTARIZATION_BLOCKS == 1 ) - fprintf(stderr,"%s nextheight.%d lag.%d\n",coin,nextheight+i,(int32_t)(time(NULL) - t)); - if ( (time(NULL) - t) < 3*NOTARIZATION_TIME/2 ) - { - nextheight += i; - nexttime = t; - break; - } - } - } - // check ongoing rounds and prevent too close ones - if ( time(NULL) > nexttime + NOTARIZATION_TIME && height > nextheight+NOTARIZATION_BLOCKS/2 && time(NULL) > prevntztime+NOTARIZATION_TIME && nexttime > prevntztime+NOTARIZATION_TIME ) - { - //if ( nexttime - dpow_roundpending(coin,acname) > NOTARIZATION_TIME ) - { - checkhash = dpow_blockhash(coin,nextheight); - chainhash = get_coinblockhash(coin,acname,nextheight); - if ( memcmp(&checkhash,&chainhash,sizeof(checkhash)) == 0 ) - { - bits256_str(hexstr,chainhash); - sprintf(&hexstr[64],"%08x",nextheight); - sprintf(&hexstr[72],"%08x",nexttime); - hexstr[80] = 0; - if ( dpow_hasmessage(hexstr,coin,"rounds",DPOW_pubkeystr) == 0 ) - { - if ( (retjson2= dpow_broadcast(priority,hexstr,coin,(char *)"rounds",DPOW_pubkeystr,"","")) != 0 ) - { - free_json(retjson2); - //fprintf(stderr,"start notarization for %s.%d when ht.%d prevntz.%d\n",coin,nextheight,height,ntzheight); - //if ( (retjson2= dpow_notarize(coin,nextheight)) != 0 ) - // free_json(retjson2); - } - } - for (i=0; i<3; i++) - { - if ( dpow_roundproposal(coin) == 0 ) - break; - sleep(3); - } - if ( i == 3 ) - fprintf(stderr,"no consensus\n"); - } else fprintf(stderr,"%s.%d: chainhash.%s != %s, must have been reorged\n",coin,nextheight,bits256_str(str,chainhash),bits256_str(str2,checkhash)); - } - } - } - } - } - } - return(0); -} - diff --git a/src/cc/dapps/oraclefeed.c b/src/cc/dapps/oraclefeed.c index 456b2d56718..ac1f8840095 100644 --- a/src/cc/dapps/oraclefeed.c +++ b/src/cc/dapps/oraclefeed.c @@ -16,26 +16,17 @@ #include #include #include -#include #include #include -#include #include "cJSON.c" -#define MARKERVALUE 1000 -#define EXTTXFEE 5000 -#define BTCTXFEE 20000 - bits256 zeroid; -static const int64_t COIN = 100000000; void myprintf(const char* format, ...) { va_list marker; va_start( marker, format ); - //vfprintf(stderr,format,marker); - vsyslog(LOG_INFO, format, marker); - fflush(stdout); + vfprintf(stdout, format, marker); fflush(stdout); va_end( marker ); } @@ -347,7 +338,7 @@ cJSON *get_cli(char *refcoin,char **retstrp,char *acname,char *method,char *arg0 //myprintf("ref.(%s) REFCOIN_CLI (%s)\n",refcoin,cmdstr); } #ifdef TESTMODE - myprintf("cmd: %s\n",cmdstr); + fprintf(stderr,"cmd: %s\n",cmdstr); #endif // TESTMODE system(cmdstr); *retstrp = 0; @@ -355,7 +346,7 @@ cJSON *get_cli(char *refcoin,char **retstrp,char *acname,char *method,char *arg0 { jsonstr[strlen(jsonstr)-1]='\0'; #ifdef TESTMODE - myprintf("jsonstr.(%s)\n",jsonstr); + fprintf(stderr,"jsonstr.(%s)\n",jsonstr); #endif // TESTMODE if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0) *retstrp = jsonstr; @@ -384,7 +375,7 @@ bits256 broadcasttx(char *refcoin,char *acname,cJSON *hexjson) retstr[64] = 0; decode_hex(txid.bytes,32,retstr); } - myprintf("broadcast %s txid.(%s)\n",strlen(acname)>0?acname:refcoin,bits256_str(str,txid)); + fprintf(stderr,"broadcast %s txid.(%s)\n",strlen(acname)>0?acname:refcoin,bits256_str(str,txid)); free(retstr); } } @@ -398,10 +389,11 @@ int32_t get_coinheight(char *refcoin,char *acname) { height = jint(retjson,"blocks"); free_json(retjson); + } else if ( retstr != 0 ) { - myprintf("%s get_coinheight.(%s) error.(%s)\n",refcoin,acname,retstr); + fprintf(stderr,"%s get_coinheight.(%s) error.(%s)\n",refcoin,acname,retstr); free(retstr); } return(height); @@ -414,7 +406,7 @@ bits256 get_coinblockhash(char *refcoin,char *acname,int32_t height) sprintf(heightstr,"%d",height); if ( (retjson= get_cli(refcoin,&retstr,acname,"getblockhash",heightstr,"","","")) != 0 ) { - myprintf("unexpected blockhash json.(%s)\n",jprint(retjson,0)); + fprintf(stderr,"unexpected blockhash json.(%s)\n",jprint(retjson,0)); free_json(retjson); } else if ( retstr != 0 ) @@ -436,71 +428,18 @@ bits256 get_coinmerkleroot(char *refcoin,char *acname,bits256 blockhash) if ( (retjson= get_cli(refcoin,&retstr,acname,"getblockheader",bits256_str(str,blockhash),"","","")) != 0 ) { merkleroot = jbits256(retjson,"merkleroot"); - //myprintf("got merkleroot.(%s)\n",bits256_str(str,merkleroot)); + //fprintf(stderr,"got merkleroot.(%s)\n",bits256_str(str,merkleroot)); free_json(retjson); } else if ( retstr != 0 ) { - myprintf("%s %s get_coinmerkleroot error.(%s)\n",refcoin,acname,retstr); + fprintf(stderr,"%s %s get_coinmerkleroot error.(%s)\n",refcoin,acname,retstr); free(retstr); } return(merkleroot); } -uint64_t get_depositbalance(char *refcoin,char *acname,char* depositaddress) -{ - cJSON *retjson,*retjson2,*unspent,*vout,*vouts; char *retstr,*retstr2,str[256],*txid; double sum=0; uint64_t balance=0; int n; - - sprintf(str,"\"[\\\"%s\\\"]\"",depositaddress); - if ( (retjson= get_cli(refcoin,&retstr,acname,"listunspent","1","9999999",str,"")) != 0 ) - { - for(int i=0;i2) - { - vout=jitem(vouts,2); - if (strcmp(jstri(jarray(&n,jobj(vout,"scriptPubKey"),"addresses"),0),depositaddress)==0) sum += jdouble(vout,"amount"); - } - free_json(retjson2); - } - else if ( retstr2 != 0 ) - { - myprintf("%s %s get_depositbalance error.(%s)\n",refcoin,acname,retstr2); - free(retstr2); - } - } - balance+=sum*COIN; - free_json(retjson); - } - else if ( retstr != 0 ) - { - myprintf("%s %s get_depositbalance error.(%s)\n",refcoin,acname,retstr); - free(retstr); - } - return(balance); -} - -int32_t get_coinheader(char *refcoin,char *acname,bits256 *blockhashp,bits256 *merklerootp, uint64_t *balance, int32_t prevheight, char* depositaddress) +int32_t get_coinheader(char *refcoin,char *acname,bits256 *blockhashp,bits256 *merklerootp,int32_t prevheight) { int32_t height = 0; char str[65]; if ( prevheight == 0 ) @@ -508,7 +447,6 @@ int32_t get_coinheader(char *refcoin,char *acname,bits256 *blockhashp,bits256 *m else height = prevheight + 1; if ( height > 0 ) { - *balance = get_depositbalance(refcoin,acname,depositaddress); *blockhashp = get_coinblockhash(refcoin,acname,height); if ( bits256_nonz(*blockhashp) != 0 ) { @@ -519,15 +457,14 @@ int32_t get_coinheader(char *refcoin,char *acname,bits256 *blockhashp,bits256 *m } memset(blockhashp,0,sizeof(*blockhashp)); memset(merklerootp,0,sizeof(*merklerootp)); - *balance=0; return(0); } -cJSON *get_gatewayspendingsignwithdraws(int8_t type,char *refcoin,char *acname,char *bindtxidstr) +cJSON *get_gatewayspending(int8_t type,char *refcoin,char *acname,char *bindtxidstr) { cJSON *retjson; char *retstr; char function[64]; - if (type==0) sprintf(function,"%s","gatewayspendingsignwithdraws"); - else if (type==1) sprintf(function,"%s","importgatewaypendingsignwithdraws"); + if (type==0) sprintf(function,"%s","gatewayspendingwithdraws"); + else if (type==1) sprintf(function,"%s","importgatewaypendingwithdraws"); if ( (retjson= get_cli(refcoin,&retstr,acname,function,bindtxidstr,refcoin,"","")) != 0 ) { //myprintf("pending.(%s)\n",jprint(retjson,0)); @@ -535,18 +472,18 @@ cJSON *get_gatewayspendingsignwithdraws(int8_t type,char *refcoin,char *acname,c } else if ( retstr != 0 ) { - myprintf("%s get_gatewayspendingsignwithdraws.(%s) error.(%s)\n",refcoin,acname,retstr); + fprintf(stderr,"%s get_gatewayspending.(%s) error.(%s)\n",refcoin,acname,retstr); free(retstr); } return(0); } -cJSON *get_gatewayssignedwithdraws(int8_t type,char *refcoin,char *acname,char *bindtxidstr) +cJSON *get_gatewaysprocessed(int8_t type,char *refcoin,char *acname,char *bindtxidstr) { cJSON *retjson; char *retstr; char function[64]; - if (type==0) sprintf(function,"%s","gatewayssignedwithdraws"); - else if (type==1) sprintf(function,"%s","importgatewaysignedwithdraws"); + if (type==0) sprintf(function,"%s","gatewaysprocessed"); + else if (type==1) sprintf(function,"%s","importgatewayprocessed"); if ( (retjson= get_cli(refcoin,&retstr,acname,function,bindtxidstr,refcoin,"","")) != 0 ) { //myprintf("pending.(%s)\n",jprint(retjson,0)); @@ -554,7 +491,7 @@ cJSON *get_gatewayssignedwithdraws(int8_t type,char *refcoin,char *acname,char * } else if ( retstr != 0 ) { - myprintf("%s get_gatewayssignedwithdraws.(%s) error.(%s)\n",refcoin,acname,retstr); + fprintf(stderr,"%s get_gatewaysprocessed.(%s) error.(%s)\n",refcoin,acname,retstr); free(retstr); } return(0); @@ -570,7 +507,7 @@ cJSON *get_rawmempool(char *refcoin,char *acname) } else if ( retstr != 0 ) { - myprintf("get_rawmempool.(%s) error.(%s)\n",acname,retstr); + fprintf(stderr,"get_rawmempool.(%s) error.(%s)\n",acname,retstr); free(retstr); } return(0); @@ -589,7 +526,7 @@ cJSON *get_addressutxos(char *refcoin,char *acname,char *coinaddr) } else if ( retstr != 0 ) { - //myprintf("get_addressutxos.(%s) error.(%s)\n",acname,retstr); + //fprintf(stderr,"get_addressutxos.(%s) error.(%s)\n",acname,retstr); free(retstr); } return(0); @@ -604,7 +541,7 @@ cJSON *get_rawtransaction(char *refcoin,char *acname,bits256 txid) } else if ( retstr != 0 ) { - myprintf("get_rawtransaction.(%s) %s error.(%s)\n",refcoin,acname,retstr); + fprintf(stderr,"get_rawtransaction.(%s) %s error.(%s)\n",refcoin,acname,retstr); free(retstr); } return(0); @@ -620,7 +557,7 @@ int32_t validateaddress(char *refcoin,char *acname,char *depositaddr, char* comp } else if ( retstr != 0 ) { - myprintf("validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); + fprintf(stderr,"validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); free(retstr); } @@ -639,30 +576,19 @@ void importaddress(char *refcoin,char *acname,char *depositaddr, char *label,int } else if ( retstr != 0 ) { - myprintf("importaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); + fprintf(stderr,"importaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); free(retstr); } } -void addmultisigaddress(char *refcoin,char *acname,int32_t M, cJSON *pubkeys) +void addmultisigaddress(char *refcoin,char *acname,int32_t M, char *pubkeys) { - cJSON *retjson; char *retstr,Mstr[10],addr[64],*keys,temp[128]; int n; + cJSON *retjson; char *retstr,Mstr[10],addr[64]; sprintf(Mstr,"%d",M); - n=cJSON_GetArraySize(pubkeys); - keys=malloc((sizeof(char)*70*n)+64); - sprintf(keys,"\"["); - for (int i=0;i= required ) break; + if ( (*totalp) >= required ) + { + if (j<3) j++; + else break; + } } } } @@ -702,34 +632,15 @@ cJSON *getinputarray(int64_t *totalp,cJSON *unspents,int64_t required) char *createrawtx(char *refcoin,char *acname,char *depositaddr,char *withdrawaddr,char *txidaddr,int64_t satoshis) { - char *retstr,*retstr2,array[128],str[12],str1[20],*txstr = 0; cJSON *retjson2,*retjson,*vins,*vouts,*upg,*upgrades; int64_t txfee,total,change = 0; - int overwinter=0,locktime=0; + char *retstr,*retstr2,array[128],*txstr = 0; cJSON *retjson2,*retjson,*vins,*vouts; int64_t txfee,total,change = 0; if ( strcmp(refcoin,"BTC") == 0 ) - txfee = BTCTXFEE; - else txfee = EXTTXFEE; + txfee = 20000; + else txfee = 10000; if ( satoshis < txfee ) { myprintf("createrawtx: satoshis %.8f < txfee %.8f\n",(double)satoshis/SATOSHIDEN,(double)txfee/SATOSHIDEN); return(0); } - if ( (retjson= get_cli(refcoin,&retstr,acname,"getblockchaininfo","","","","")) != 0 ) - { - if ((upgrades=jobj(retjson,"upgrades"))!=0) - { - upg=upgrades->child; - while (upg) - { - if (strcmp(jstr(upg,"name"),"Overwinter")==0) overwinter=jint(retjson,"blocks")+1440; - upg=upg->next; - } - } - free_json(retjson); - } - if ( (retjson= get_cli(refcoin,&retstr,acname,"getinfo","","","","")) != 0 ) - { - locktime=jint(retjson,"tiptime"); - free_json(retjson); - } sprintf(array,"\'[\"%s\"]\'",depositaddr); if ( (retjson= get_cli(refcoin,&retstr,acname,"listunspent","1","99999999",array,"")) != 0 ) { @@ -739,8 +650,8 @@ char *createrawtx(char *refcoin,char *acname,char *depositaddr,char *withdrawadd if ( total >= satoshis ) { vouts = cJSON_CreateObject(); - jaddnum(vouts,withdrawaddr,(double)(satoshis-txfee-(MARKERVALUE))/SATOSHIDEN); - jaddnum(vouts,txidaddr,(double)(MARKERVALUE)/SATOSHIDEN); + jaddnum(vouts,withdrawaddr,(double)(satoshis-2*txfee)/SATOSHIDEN); + jaddnum(vouts,txidaddr,(double)txfee/SATOSHIDEN); if ( total > satoshis) { change = (total - satoshis); @@ -751,10 +662,8 @@ char *createrawtx(char *refcoin,char *acname,char *depositaddr,char *withdrawadd char *argA=malloc(sizeof(char) * (strlen(tmpA)+3)); char *argB=malloc(sizeof(char) * (strlen(tmpB)+3)); sprintf(argA,"\'%s\'",tmpA); - sprintf(argB,"\'%s\'",tmpB); - sprintf(str,"%d",locktime); - sprintf(str1,"%d",overwinter); - if ( (retjson2= get_cli(refcoin,&txstr,acname,"createrawtransaction",argA,argB,str,overwinter?str1:"")) != 0 ) + sprintf(argB,"\'%s\'",tmpB); + if ( (retjson2= get_cli(refcoin,&txstr,acname,"createrawtransaction",argA,argB,"","")) != 0 ) { myprintf("createrawtx: unexpected JSON2.(%s)\n",jprint(retjson2,0)); free_json(retjson2); @@ -801,12 +710,32 @@ cJSON *addsignature(char *refcoin,char *acname,char *rawtx, int M) return(0); } -bits256 gatewayswithdrawsign(int8_t type,char *refcoin,char *acname,bits256 withtxid,char *hex) +bits256 gatewayspartialsign(int8_t type,char *refcoin,char *acname,bits256 txid,char *hex) +{ + char str[65],*retstr; cJSON *retjson; char function[64]; + if (type==0) sprintf(function,"%s","gatewayspartialsign"); + else if (type==1) sprintf(function,"%s","importgatewaypartialsign"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bits256_str(str,txid),refcoin,hex,"")) != 0 ) + { + if (strcmp("error",jstr(retjson,"result"))!=0) txid=broadcasttx(refcoin,acname,retjson); + else myprintf("%s\n",jstr(retjson,"error")); + free(retjson); + return (txid); + } + else if ( retstr != 0 ) + { + myprintf("error parsing gatewayspartialsing.(%s)\n",retstr); + free(retstr); + } + return (zeroid); +} + +bits256 gatewayscompletesigning(int8_t type,char *refcoin,char *acname,bits256 withtxid,char *hex) { char str[65],*retstr; cJSON *retjson; bits256 txid; char function[64]; - if (type==0) sprintf(function,"%s","gatewayswithdrawsign"); - else if (type==1) sprintf(function,"%s","importgatewaywithdrawsign"); + if (type==0) sprintf(function,"%s","gatewayscompletesigning"); + else if (type==1) sprintf(function,"%s","importgatewaycompletesigning"); if ( (retjson= get_cli(refcoin,&retstr,acname,function,bits256_str(str,withtxid),refcoin,hex,"")) != 0 ) { if (strcmp("error",jstr(retjson,"result"))!=0) txid=broadcasttx(refcoin,acname,retjson); @@ -816,7 +745,7 @@ bits256 gatewayswithdrawsign(int8_t type,char *refcoin,char *acname,bits256 with } else if ( retstr != 0 ) { - myprintf("error parsing gatewayswithdrawsign.(%s)\n",retstr); + myprintf("error parsing gatewayscompletesigning.(%s)\n",retstr); free(retstr); } return (zeroid); @@ -843,15 +772,15 @@ bits256 gatewaysmarkdone(int8_t type,char *refcoin,char *acname,bits256 withtxid return (zeroid); } -int32_t get_gatewaysinfo(int8_t type,char *refcoin,char *acname,char *depositaddr,int32_t *Mp,int32_t *Np,char *bindtxidstr,char *coin,char *oraclestr, cJSON *pubkeys) +int32_t get_gatewaysinfo(int8_t type,char *refcoin,char *acname,char *depositaddr,int32_t *Mp,int32_t *Np,char *bindtxidstr,char *coin,char *oraclestr, char **pubkeys) { - char *oracle,*retstr,*name,*deposit; cJSON *retjson,*pubarray; int32_t n; char function[64]; + char *oracle,*retstr,*name,*deposit,temp[128]; cJSON *retjson,*pubarray; int32_t n; char function[64]; if (type==0) sprintf(function,"%s","gatewaysinfo"); else if (type==1) sprintf(function,"%s","importgatewayinfo"); if ( (retjson= get_cli(refcoin,&retstr,acname,function,bindtxidstr,"","","")) != 0 ) { - if ( (oracle= jstr(retjson,"oracletxid")) != 0 && strcmp(oracle,oraclestr) == 0 && (deposit= jstr(retjson,"depositaddr")) != 0 ) + if ( (oracle= jstr(retjson,"oracletxid")) != 0 && strcmp(oracle,oraclestr) == 0 && (deposit= jstr(retjson,"deposit")) != 0 ) { strcpy(depositaddr,deposit); if ( jstr(retjson,"coin") != 0 && strcmp(jstr(retjson,"coin"),coin) == 0 && jint(retjson,"N") >= 1 ) @@ -860,10 +789,18 @@ int32_t get_gatewaysinfo(int8_t type,char *refcoin,char *acname,char *depositadd *Np = jint(retjson,"N"); } else myprintf("coin.%s vs %s\n",jstr(retjson,"coin"),coin); - pubarray=jarray(&n,retjson,"pubkeys"); - for (int i=0;i prevheight ) + if ( (height= get_coinheader(refcoin,acname,&blockhash,&merkleroot,prevheight)) > prevheight ) { for (i=0; i<4; i++) sprintf(&hexstr[i*2],"%02x",(uint8_t)((height >> (i*8)) & 0xff)); @@ -1082,9 +1023,7 @@ int32_t get_oracledata(char *refcoin,char *acname,int32_t prevheight,char* depos sprintf(&hexstr[8 + (31-i)*2],"%02x",blockhash.bytes[i]); for (i=31; i>=0; i--) sprintf(&hexstr[8 + 64 + (31-i)*2],"%02x",merkleroot.bytes[i]); - for (i=0; i<8; i++) - sprintf(&hexstr[8 + 64*2 + i*2],"%02x",(uint8_t)((balance >> (i*8)) & 0xff)); - hexstr[8 + 64*2 + 16] = 0; + hexstr[8 + 64*2] = 0; return(height); } } @@ -1117,15 +1056,10 @@ oraclesdata 17a841a919c284cea8a676f34e793da002e606f19a9258a3190bed12d5aaa3ff 034 int32_t main(int32_t argc,char **argv) { - cJSON *clijson,*clijson2,*regjson,*item,*pubkeys; int32_t type,i,retval,M,N,height,prevheight = 0; - char *format,*acname,*oraclestr,*bindtxidstr,*pkstr,*pubstr,*retstr,*retstr2,depositaddr[64],hexstr[4096],refcoin[64]; - uint64_t price; bits256 txid; time_t seconds; - - openlog (NULL, LOG_CONS | LOG_NDELAY, LOG_USER); - memset(&zeroid,0,sizeof(zeroid)); - if ( argc < 7 ) + cJSON *clijson,*clijson2,*regjson,*item; int32_t type,i,retval,M,N,n,height,prevheight = 0; char *pubkeys,*format,*acname,*oraclestr,*bindtxidstr,*pkstr,*pubstr,*retstr,*retstr2,depositaddr[64],hexstr[4096],refcoin[64]; uint64_t price; bits256 txid; + if ( argc < 6 ) { - myprintf("usage: oraclefeed $ACNAME $ORACLETXID $MYPUBKEY $FORMAT $BINDTXID $STARTINGHEIGHT [refcoin_cli]\n"); + myprintf("usage: oraclefeed $ACNAME $ORACLETXID $MYPUBKEY $FORMAT $BINDTXID [refcoin_cli]\n"); return(-1); } myprintf("Powered by CoinDesk (%s) %.8f\n","https://www.coindesk.com/price/",dstr(get_btcusd())); @@ -1134,55 +1068,53 @@ int32_t main(int32_t argc,char **argv) pkstr = argv[3]; format = argv[4]; bindtxidstr = argv[5]; - prevheight = atoi(argv[6]); - if ( argc > 7 ) - REFCOIN_CLI = argv[7]; + if ( argc > 6 ) + REFCOIN_CLI = argv[6]; else REFCOIN_CLI = "./komodo-cli"; - if ( strncmp(format,"IhhL",4) != 0 && format[0] != 'L' ) + if ( strncmp(format,"Ihh",3) != 0 && format[0] != 'L' ) { - myprintf("only formats of L and IhhL are supported now\n"); + myprintf("only formats of L and Ihh are supported now\n"); return(-1); } M = N = 0; refcoin[0] = 0; - retstr = 0; - if ((clijson= get_cli(refcoin,&retstr,acname,"oraclesinfo",oraclestr,"","","")) != 0 ) + while ( 1 ) { - if ( refcoin[0] == 0 && jstr(clijson,"name") != 0 ) + retstr = 0; + if ( (refcoin[0] == 0 || prevheight < (get_coinheight(refcoin,"") - 10)) && (clijson= get_cli(refcoin,&retstr,acname,"oraclesinfo",oraclestr,"","","")) != 0 ) { - strcpy(refcoin,jstr(clijson,"name")); - if ( strcmp("KMD",refcoin) != 0 && argc != 7 ) - { - myprintf("need to specify path to refcoin's cli as last argv\n"); - exit(0); - } - pubkeys=cJSON_CreateArray(); - if ( get_gatewaysinfo(0,refcoin,acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr,pubkeys) == 0 ) type=0; - else if ( get_gatewaysinfo(1,refcoin,acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr,pubkeys) == 0 ) type=1; - else + if ( refcoin[0] == 0 && jstr(clijson,"name") != 0 ) { - myprintf("cant find bindtxid.(%s)\n",bindtxidstr); - exit(0); + strcpy(refcoin,jstr(clijson,"name")); + if ( strcmp("KMD",refcoin) != 0 && argc != 7 ) + { + myprintf("need to specify path to refcoin's cli as last argv\n"); + exit(0); + } + pubkeys=0; + if ( get_gatewaysinfo(0,refcoin,acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr,&pubkeys) == 0 ) type=0; + else if ( get_gatewaysinfo(1,refcoin,acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr,&pubkeys) == 0 ) type=1; + else + { + myprintf("cant find bindtxid.(%s)\n",bindtxidstr); + exit(0); + } + if (validateaddress(refcoin,"",depositaddr,"iswatchonly")==0 && validateaddress(refcoin,"",depositaddr,"ismine")==0) + { + if (M==1 && N==1) importaddress(refcoin,"",depositaddr,bindtxidstr,0); + else addmultisigaddress(refcoin,"",M,pubkeys); + } + if (pubkeys!=0) free(pubkeys); + myprintf("set refcoin %s <- %s [%s] M.%d of N.%d\n",depositaddr,refcoin,REFCOIN_CLI,M,N); } - if (validateaddress(refcoin,"",depositaddr,"iswatchonly")==0 && validateaddress(refcoin,"",depositaddr,"ismine")==0) + if ( (regjson= jarray(&n,clijson,"registered")) != 0 ) { - if (M==1 && N==1) importaddress(refcoin,"",depositaddr,bindtxidstr,0); - else addmultisigaddress(refcoin,"",M,pubkeys); - } - myprintf("set refcoin %s <- %s [%s] M.%d of N.%d\n",depositaddr,refcoin,REFCOIN_CLI,M,N); - } - while (1) - { - for (i=0; ibase : &mp->rel; - if ( coin[0] == 0 ) - return(coin); - if ( (external= jarray(&n,SUBATOMIC_json,"externalcoins")) != 0 && n > 0 ) - { - for (i=0; icli) ) - { - ptr->isexternal = 1; - //strcpy(ptr->cli,clistr); - banned method. - snprintf(ptr->cli,strlen(clistr) + 1,"%s",clistr); //guaranteed to null terminate the string - fprintf(stderr,"found external coin %s %s %s\n",coin,clistr,ptr->cli); //debug 333 - } - } - } - if ( coin[0] == '#' ) - { - snprintf(ptr->coinstr,strlen(coin) + 1,"%s",coin); - snprintf(ptr->acname,strlen("") + 1,"%s",""); - ptr->isfile = 1; - return(coin); - } - else if ( coin[0] != 'z' ) - { - for (i=1; coin[i]!=0; i++) - if ( coin[i] == '.' ) - { - dpow_tokenregister(ptr->tokenid,0,coin,0); - if ( ptr->tokenid[0] != 0 ) - { - snprintf(tmpstr,strlen(coin) + 1,"%s",coin); - tmpstr[i] = 0; - //fprintf(stderr,"found a tokenmap %s -> %s %s\n",coin,tmpstr,ptr->tokenid); - ptr->istoken = 1; - snprintf(ptr->acname,strlen(coin) + 1,"%s",coin); - snprintf(ptr->coinstr,strlen("") + 1,"%s",""); - return(tmpstr); - } - } - if ( ptr->isexternal == 0 ) - { - if ( strcmp(coin,"KMD") != 0 ) - { - snprintf(ptr->acname,strlen(coin) + 1,"%s",coin); - snprintf(ptr->coinstr,strlen("") + 1,"%s",""); - } - else - { - snprintf(ptr->coinstr,strlen(coin) + 1,"%s",coin); - snprintf(ptr->acname,strlen("") + 1,"%s",""); - } - } - else - { - snprintf(ptr->coinstr,strlen(coin) + 1,"%s",coin); - snprintf(ptr->acname,strlen("") + 1,"%s",""); - } - return(coin); - } - else - { - for (i=1; coin[i]!=0; i++) - if ( isupper(coin[i]) == 0 ) - return(coin); - if ( strcmp(coin+1,"KMD") != 0 ) - ptr->iszaddr = 1; - return(coin+1); - } -} - -int32_t subatomic_zonly(struct coininfo *coin) -{ - if ( strcmp(coin->coin,"PIRATE") == 0 ) - return(1); - else return(coin->iszaddr); -} - -// //////////////////////////////// the four key functions needed to support a new item for subatomics - -int64_t _subatomic_getbalance(struct coininfo *coin) -{ - cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; - if ( (retjson= subatomic_cli(coin->coin,&retstr,"getbalance","","","","","","","")) != 0 ) - { - fprintf(stderr,"_subatomic_getbalance.(%s) %s returned json!\n",coin->coinstr,coin->cli); - free_json(retjson); - } - else if ( retstr != 0 ) - { - amount = atof(retstr) * SATOSHIDEN; - sprintf(cmpstr,"%.8f",dstr(amount)); - if ( strcmp(retstr,cmpstr) != 0 ) - amount++; - //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); - free(retstr); - } - return (amount); -} - -bits256 _subatomic_sendtoaddress(struct coininfo *coin,char *destaddr,int64_t satoshis) -{ - char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid; - memset(txid.bytes,0,sizeof(txid)); - sprintf(numstr,"%.8f",(double)satoshis/SATOSHIDEN); - if ( (retjson= subatomic_cli(coin->coin,&retstr,"sendtoaddress",destaddr,numstr,"false","","","","")) != 0 ) - { - fprintf(stderr,"unexpected _subatomic_sendtoaddress json.(%s)\n",jprint(retjson,0)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - if ( strlen(retstr) >= 64 ) - { - retstr[64] = 0; - decode_hex(txid.bytes,32,retstr); - } - fprintf(stderr,"_subatomic_sendtoaddress %s %.8f txid.(%s)\n",destaddr,(double)satoshis/SATOSHIDEN,bits256_str(str,txid)); - free(retstr); - } - return(txid); -} - -cJSON *_subatomic_rawtransaction(struct coininfo *coin,bits256 txid) -{ - cJSON *retjson; char *retstr,str[65]; - if ( (retjson= subatomic_cli(coin->coin,&retstr,"getrawtransaction",bits256_str(str,txid),"1","","","","","")) != 0 ) - { - return(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"_subatomic_rawtransaction.(%s) %s error.(%s)\n",coin->coin,coin->name,retstr); - free(retstr); - } - return(0); -} - -int64_t subatomic_getbalance(struct coininfo *coin) -{ - char *coinstr,*acname=""; FILE *fp; int64_t retval = 0; - if ( strcmp(coin->coin,"KMD") != 0 ) - { - acname = coin->coin; - coinstr = ""; - } else coinstr = coin->coin; - if ( coin->isfile != 0 ) - { - if ( (fp= fopen(coin->name+1,"rb")) != 0 ) // if alice, add bob pubkey to fname - { - fclose(fp); - retval = SATOSHIDEN; - } - return(retval); - } - else if ( subatomic_zonly(coin) != 0 ) - return(z_getbalance(coinstr,acname,DPOW_recvZaddr)); - else - { - if ( coin->istoken != 0 ) - { - if ( get_getbalance(coinstr,acname) < SUBATOMIC_TXFEE ) - { - fprintf(stderr,"not enough balance to send token\n"); - return(0); - } - //fprintf(stderr,"token balance %s\n",coin->tokenid); - return(get_tokenbalance(coinstr,acname,coin->tokenid) * SATOSHIDEN); - } - else if ( coin->isexternal == 0 ) - return(get_getbalance(coinstr,acname)); - else return(_subatomic_getbalance(coin)); - } -} - -bits256 subatomic_coinpayment(uint32_t origid,int32_t OTCmode,struct coininfo *coin,char *destaddr,uint64_t paytoshis,char *memostr,char *destpub,char *senderpub) -{ - bits256 txid; char opidstr[128],opretstr[32],str[65],*status,*coinstr,*acname=""; cJSON *retjson,*retjson2,*item,*res; int32_t i,pending=0; - memset(&txid,0,sizeof(txid)); - if ( OTCmode == 0 ) - { - fprintf(stderr,"micropayment channels are not supported yet\n"); - return(txid); - } - if ( coin->isfile != 0 ) - { - fprintf(stderr,"start broadcast of (%s)\n",coin->coin+1); - if ( (retjson= dpow_publish(SUBATOMIC_PRIORITY,coin->coin+1)) != 0 ) // spawn thread - { - sprintf(opretstr,"%08x",juint(retjson,"id")); - sprintf(opidstr,"%u",origid); - if ( (retjson2= dpow_broadcast(SUBATOMIC_PRIORITY,opretstr,"inbox",opidstr,senderpub,"","")) != 0 ) - free_json(retjson2); - fprintf(stderr,"broadcast file.(%s) and send id.%u to alice (%s)\n",coin->coin+1,juint(retjson,"id"),jprint(retjson,0)); - txid = jbits256(retjson,"filehash"); - free_json(retjson); - } - fprintf(stderr,"end broadcast of (%s) to %s\n",coin->coin+1,senderpub); - return(txid); - } - else if ( subatomic_zonly(coin) != 0 ) - { - if ( memostr[0] == 0 ) - memostr = "beef"; - z_sendmany(opidstr,"",coin->coin,DPOW_recvZaddr,destaddr,paytoshis,memostr); - for (i=0; icoin,opidstr)) != 0 ) - { - item = jitem(retjson,0); - if ( (status= jstr(item,"status")) != 0 ) - { - if ( strcmp(status,"executing") == 0 ) - pending++; - else - { - res = jobj(item,"result"); - txid = jbits256(res,"txid"); - //fprintf(stderr,"got Ztx txid.%s\n",bits256_str(str,txid)); - free_json(retjson); - break; - } - /*else if ( clearresults != 0 ) - { - if ( (result= z_getoperationresult(coinstr,"",jstri(array,i))) != 0 ) - { - free_json(result); - } - }*/ - } - free_json(retjson); - } - sleep(1); - } - if ( i == 60 ) - printf("%u timed out waiting for opid to finish\n",origid); - } - else - { - if ( strcmp(coin->coin,"KMD") != 0 ) - { - acname = coin->coin; - coinstr = ""; - } else coinstr = coin->coin; - if ( coin->istoken != 0 ) - txid = tokentransfer(coinstr,acname,coin->tokenid,destpub,paytoshis/SATOSHIDEN); - else if ( coin->isexternal == 0 ) - { - sprintf(opretstr,"%08x",origid); - txid = sendtoaddress(coinstr,acname,destaddr,paytoshis,opretstr); - } else txid = _subatomic_sendtoaddress(coin,destaddr,paytoshis); - printf("%u got txid.%s\n",origid,bits256_str(str,txid)); - } - return(txid); -} - -cJSON *subatomic_txidwait(struct coininfo *coin,bits256 txid,char *hexstr,int32_t numseconds,char *senderpub) -{ - int32_t i,zflag; char *coinstr,str[65],*acname=""; cJSON *rawtx; bits256 z; bits256 filehash; - memset(&z,0,sizeof(z)); - if ( memcmp(&z,&txid,sizeof(txid)) == 0 ) - return(0); - if ( hexstr != 0 && hexstr[0] != 0 ) // probably not worth doing and zaddr is a problem to decode - { - // compare against txid - // if matches, sendrawtransaction if OTC mode, decoode and return if channels mode - } - zflag = (subatomic_zonly(coin) != 0); - if ( strcmp(coin->coin,"KMD") != 0 ) - { - acname = coin->coin; - coinstr = ""; - } else coinstr = coin->coin; - for (i=0; iisfile != 0 ) - { - if ( (rawtx= dpow_subscribe(SUBATOMIC_PRIORITY,coin->coin+1,senderpub)) != 0 ) - { - filehash = jbits256(rawtx,"filehash"); - if ( memcmp(&filehash,&txid,sizeof(filehash)) != 0 ) - { - fprintf(stderr,"waiting (%s) (%s)\n",coin->coin+1,jprint(rawtx,0)); - free_json(rawtx); - rawtx = 0; - } else return(rawtx); - } - } - else if ( zflag != 0 ) - rawtx = get_z_viewtransaction(coinstr,acname,txid); - else if ( coin->isexternal == 0 ) - rawtx = get_rawtransaction(coinstr,acname,txid); - else rawtx = _subatomic_rawtransaction(coin,txid); - if ( rawtx != 0 ) - return(rawtx); - sleep(1); - } - printf("%s/%s timeout waiting for %s\n",coin->name,coin->coin,bits256_str(str,txid)); - return(0); -} - -int64_t subatomic_verifypayment(struct coininfo *coin,cJSON *rawtx,uint64_t destsatoshis,char *destaddr,bits256 txid) -{ - int32_t i,n,m,valid=0; bits256 tokenid,filehash,checkhash; cJSON *array,*item,*sobj,*a; char *addr,*acname,*coinstr,tokenaddr[64],*hex; uint8_t hexbuf[512],pub33[33]; uint64_t netval,recvsatoshis = 0; - if ( coin->isfile != 0 ) - { - filehash = jbits256(rawtx,"filehash"); - checkhash = jbits256(rawtx,"checkhash"); - if ( memcmp(&txid,&filehash,sizeof(txid)) == 0 && memcmp(&txid,&checkhash,sizeof(txid)) == 0 ) - { - fprintf(stderr,"verified file is matching the filehash (%s)\n",jprint(rawtx,0)); - return(SATOSHIDEN); - } else return(0); - } - else if ( subatomic_zonly(coin) != 0 ) - { - if ( (array= jarray(&n,rawtx,"outputs")) != 0 && n > 0 ) - { - for (i=0; iistoken != 0 ) - { - if ( (array= jarray(&n,rawtx,"vout")) != 0 && n > 0 ) - { - item = jitem(array,0); - if ( (sobj= jobj(item,"scriptPubKey")) != 0 && (a= jarray(&m,sobj,"addresses")) != 0 && m == 1 ) - { - if ( strcmp(coin->coin,"KMD") != 0 ) - { - acname = coin->coin; - coinstr = ""; - } else coinstr = coin->coin; - if ( get_tokenaddress(coinstr,acname,tokenaddr) != 0 ) - { - //fprintf(stderr,"tokenaddr.%s\n",tokenaddr); - if ( (addr= jstri(a,0)) != 0 && strcmp(addr,tokenaddr) == 0 ) - recvsatoshis += SATOSHIDEN * (uint64_t)(jdouble(item,"value")*SATOSHIDEN + 0.000000004999); - else fprintf(stderr,"miscompare (%s) vs %s\n",jprint(sobj,0),addr); - } - } - item = jitem(array,n-1); - if ( (sobj= jobj(item,"scriptPubKey")) != 0 && (hex= jstr(sobj,"hex")) != 0 && (m= is_hexstr(hex,0)) > 1 && m/2 < sizeof(hexbuf) ) - { - m >>= 1; - decode_hex(hexbuf,m,hex); - decode_hex(tokenid.bytes,32,coin->tokenid); - decode_hex(pub33,33,DPOW_secpkeystr); - // opret 69len EVAL_TOKENS 't' tokenid 1 33 pub33 - if ( hexbuf[0] == 0x6a && hexbuf[1] == 0x45 && hexbuf[2] == 0xf2 && hexbuf[3] == 't' && memcmp(&hexbuf[4],&tokenid,sizeof(tokenid)) == 0 && hexbuf[4+32] == 1 && hexbuf[4+32+1] == 33 && memcmp(&hexbuf[4+32+2],pub33,33) == 0 ) - { - valid = 1; - //fprintf(stderr,"validated it is a token transfer!\n"); - } else fprintf(stderr,"need to validate tokentransfer.(%s) %s %d\n",hex,DPOW_secpkeystr,memcmp(&hexbuf[4+32+2],pub33,33) == 0); - //6a 45 f2 74 2b1feef719ecb526b07416dd432bce603ac6dc8bfe794cddf105cb52f6aae3cd 01 21 02b27de3ee5335518b06f69f4fbabb029cfc737613b100996841d5532b324a5a61 - - } - recvsatoshis *= valid; - } - } - else - { - if ( (array= jarray(&n,rawtx,"vout")) != 0 && n > 0 ) - { - for (i=0; iorigid = origid; - HASH_ADD(hh,Messages,origid,sizeof(origid),mp); - return(mp); -} - -int32_t subatomic_status(struct msginfo *mp,int32_t status) -{ - static FILE *fp; - if ( fp == 0 ) - { - int32_t i,oid,s,n,num,count; struct msginfo *m; long fsize; - if ( (fp= fopen("SUBATOMIC.DB","rb+")) == 0 ) - { - if ( (fp= fopen("SUBATOMIC.DB","wb")) == 0 ) - { - fprintf(stderr,"cant create SUBATOMIC.DB\n"); - exit(-1); - } - } - else - { - fseek(fp,0,SEEK_END); - fsize = ftell(fp); - if ( (fsize % (sizeof(uint32_t)*2)) != 0 ) - { - fprintf(stderr,"SUBATOMIC.DB illegal filesize.%ld\n",fsize); - exit(-1); - } - n = (int32_t)(fsize / (sizeof(uint32_t)*2)); - rewind(fp); - for (i=num=count=0; i SUBATOMIC_CLOSED ) - { - fprintf(stderr,"SUBATOMIC.DB corrupted at filepos.%ld: illegal status.%d\n",ftell(fp),s); - exit(-1); - } - //fprintf(stderr,"%u <- %d\n",oid,s); - if ( (m= subatomic_find(oid)) == 0 ) - { - m = subatomic_add(oid); - count++; - } - if ( s > m->status ) - { - m->status = s; - num++; - } - } - fprintf(stderr,"initialized %d messages, updated %d out of total.%d\n",count,num,n); - } - } - if ( mp->status >= status ) - return(-1); - if ( fwrite(&mp->origid,1,sizeof(mp->origid),fp) != sizeof(mp->origid) || fwrite(&status,1,sizeof(status),fp) != sizeof(status) ) - fprintf(stderr,"error updating SUBATOMIC.DB, risk of double spends\n"); - fflush(fp); - mp->status = status; - return(0); -} - -struct msginfo *subatomic_tracker(uint32_t origid) -{ - struct msginfo *mp; - if ( (mp= subatomic_find(origid)) == 0 ) - { - mp = subatomic_add(origid); - subatomic_status(mp,0); - } - return(mp); -} - -char *subatomic_hexstr(char *jsonstr) -{ - char *hexstr; int32_t i,c,n = (int32_t)strlen(jsonstr); - hexstr = malloc(2*n + 3); - snprintf(hexstr,strlen(jsonstr) + 1,"%s",jsonstr); - for (i=0; iorigid); - jaddnum(item,"price",mp->price); - jaddnum(item,"openrequest",mp->openrequestid); - jaddstr(item,"base",mp->base.name); - jaddstr(item,"basecoin",mp->base.coin); - jadd64bits(item,"basesatoshis",mp->base.satoshis); - jadd64bits(item,"basetxfee",mp->base.txfee); - jadd64bits(item,"maxbaseamount",mp->base.maxamount); - jaddstr(item,"rel",mp->rel.name); - jaddstr(item,"relcoin",mp->rel.coin); - jadd64bits(item,"relsatoshis",mp->rel.satoshis); - jadd64bits(item,"reltxfee",mp->rel.txfee); - jadd64bits(item,"maxrelamount",mp->rel.maxamount); - jaddstr(item,"alice",mp->alice.pubkey); - jaddstr(item,"alicesecp",mp->alice.secp); - jaddstr(item,"bob",mp->bob.pubkey); - jaddstr(item,"bobsecp",mp->bob.secp); - if ( subatomic_zonly(&mp->rel) != 0 ) - jaddstr(item,"bobZaddr",mp->bob.recvZaddr); - else jaddstr(item,"bobaddr",mp->bob.recvaddr); - if ( mp->rel.istoken != 0 ) - jaddstr(item,"bobtoken",mp->rel.tokenid); - if ( subatomic_zonly(&mp->base) != 0 ) - jaddstr(item,"aliceZaddr",mp->alice.recvZaddr); - else jaddstr(item,"aliceaddr",mp->alice.recvaddr); - if ( mp->base.istoken != 0 ) - jaddstr(item,"alicetoken",mp->base.tokenid); - return(item); -} - -uint64_t subatomic_orderbook_mpset(struct msginfo *mp,char *basecheck) -{ - cJSON *retjson; char *tagA,*tagB,*senderpub,*str,tmpstr[32]; int32_t matches=0; double volA,volB; int64_t txfee=0; - snprintf(mp->base.name,strlen(basecheck) + 1,"%s",basecheck); - snprintf(mp->base.coin,strlen(subatomic_checkname(tmpstr,mp,0,basecheck)) + 1,"%s",subatomic_checkname(tmpstr,mp,0,basecheck)); - mp->rel.txfee = subatomic_txfee(mp->rel.coin); - if ( (retjson= dpow_get(mp->origid)) != 0 ) - { - //fprintf(stderr,"dpow_get.(%s) (%s/%s)\n",jprint(retjson,0),mp->base.coin,mp->rel.coin); - if ( (senderpub= jstr(retjson,"senderpub")) != 0 && is_hexstr(senderpub,0) == 66 && (tagA= jstr(retjson,"tagA")) != 0 && (tagB= jstr(retjson,"tagB")) != 0 && strncmp(tagB,mp->rel.name,strlen(mp->rel.name)) == 0 && strlen(tagA) < sizeof(mp->base.name) ) - { - snprintf(mp->base.name,strlen(tagA) + 1,"%s",tagA); - snprintf(mp->base.coin,strlen(subatomic_checkname(tmpstr,mp,0,tagA)) + 1,"%s",subatomic_checkname(tmpstr,mp,0,tagA)); - if ( basecheck[0] == 0 || strncmp(basecheck,tagA,strlen(basecheck)) == 0 ) - matches = 1; - else if ( strcmp(tagA,mp->base.name) == 0 ) - matches = 1; - else if ( mp->bobflag != 0 && tagA[0] == '#' && strcmp(mp->base.name,"#allfiles") == 0 ) - matches = 1; - if ( matches != 0 ) - { - if ( (str= jstr(retjson,"decrypted")) != 0 && strlen(str) < 128 ) - snprintf(mp->payload,strlen(str) + 1,"%s",str); - mp->locktime = juint(retjson,"timestamp") + SUBATOMIC_LOCKTIME; - mp->base.txfee = subatomic_txfee(mp->base.coin); - snprintf(mp->senderpub,strlen(senderpub) + 1,"%s",senderpub); - volB = jdouble(retjson,"amountB"); - volA = jdouble(retjson,"amountA"); - mp->base.maxamount = volA*SATOSHIDEN + 0.0000000049999; - mp->rel.maxamount = volB*SATOSHIDEN + 0.0000000049999; - if ( 0 && mp->rel.istoken == 0 ) - txfee = mp->rel.txfee; - if ( mp->base.maxamount != 0 && mp->rel.maxamount != 0 && volA > SMALLVAL && volB > SMALLVAL && mp->rel.satoshis <= mp->rel.maxamount ) - { - mp->price = volA / volB; - mp->base.satoshis = (mp->rel.satoshis - txfee) * mp->price; - //fprintf(stderr,"base satoshis.%llu\n",(long long)mp->base.satoshis); - } else fprintf(stderr,"%u rel %llu vs (%llu %llu)\n",mp->origid,(long long)mp->rel.satoshis,(long long)mp->base.maxamount,(long long)mp->rel.maxamount); - } else printf("%u didnt match (%s) tagA.%s %s, tagB.%s %s %d %d\n",mp->origid,basecheck,tagA,mp->base.name,tagB,mp->rel.name,tagA[0] == '#', strcmp(mp->base.name,"#allfiles") == 0); - } else printf("%u didnt compare tagA.%s %s, tagB.%s %s\n",mp->origid,tagA,mp->base.name,tagB,mp->rel.name); - free_json(retjson); - } - return(mp->base.satoshis); -} - -char *randhashstr(char *str) -{ - bits256 rands; int32_t i; - for (i=0; i<32; i++) - rands.bytes[i] = rand() >> 17; - bits256_str(str,rands); - return(str); -} - -void subatomic_extrafields(cJSON *dest,cJSON *src) -{ - char *str; - if ( (str= jstr(src,"approval")) != 0 ) - jaddstr(dest,"approval",str); - if ( (str= jstr(src,"opened")) != 0 ) - jaddstr(dest,"opened",str); - if ( (str= jstr(src,"payamount")) != 0 ) - jaddstr(dest,"payamount",str); - if ( (str= jstr(src,"destaddr")) != 0 ) - jaddstr(dest,"destaddr",str); - if ( (str= jstr(src,"bobpayment")) != 0 ) - jaddstr(dest,"bobpayment",str); - if ( (str= jstr(src,"alicepayment")) != 0 ) - jaddstr(dest,"alicepayment",str); - if ( (str= jstr(src,"bobaddr")) != 0 ) - jaddstr(dest,"bobaddr",str); - if ( (str= jstr(src,"bobZaddr")) != 0 ) - jaddstr(dest,"bobZaddr",str); - if ( (str= jstr(src,"aliceaddr")) != 0 ) - jaddstr(dest,"aliceaddr",str); - if ( (str= jstr(src,"aliceZaddr")) != 0 ) - jaddstr(dest,"aliceZaddr",str); - if ( (str= jstr(src,"alicetoken")) != 0 ) - jaddstr(dest,"alicetoken",str); - if ( (str= jstr(src,"bobtoken")) != 0 ) - jaddstr(dest,"bobtoken",str); -} - -char *subatomic_submit(cJSON *argjson,int32_t tobob) -{ - char *jsonstr,*hexstr; - jaddnum(argjson,"tobob",tobob != 0); - jsonstr = jprint(argjson,1); - hexstr = subatomic_hexstr(jsonstr); - free(jsonstr); - return(hexstr); -} - -#define SCRIPT_OP_IF 0x63 -#define SCRIPT_OP_ELSE 0x67 -#define SCRIPT_OP_DUP 0x76 -#define SCRIPT_OP_ENDIF 0x68 -#define SCRIPT_OP_TRUE 0x51 -#define SCRIPT_OP_2 0x52 -#define SCRIPT_OP_3 0x53 -#define SCRIPT_OP_DROP 0x75 -#define SCRIPT_OP_EQUALVERIFY 0x88 -#define SCRIPT_OP_HASH160 0xa9 -#define SCRIPT_OP_EQUAL 0x87 -#define SCRIPT_OP_CHECKSIG 0xac -#define SCRIPT_OP_CHECKMULTISIG 0xae -#define SCRIPT_OP_CHECKMULTISIGVERIFY 0xaf -#define SCRIPT_OP_CHECKLOCKTIMEVERIFY 0xb1 - -int32_t subatomic_redeemscript(char *redeemscript,uint32_t locktime,char *pubkeyA,char *pubkeyB) // not needed -{ - // if ( refund ) OP_HASH160 <2of2 multisig hash> OP_EQUAL // standard multisig - // else CLTV OP_DROP OP_CHECKSIG // standard spend - uint8_t pubkeyAbytes[33],pubkeyBbytes[33],hex[4096]; int32_t i,n = 0; - decode_hex(pubkeyAbytes,33,pubkeyA); - decode_hex(pubkeyBbytes,33,pubkeyB); - hex[n++] = SCRIPT_OP_IF; - hex[n++] = SCRIPT_OP_2; - hex[n++] = 33, memcpy(&hex[n],pubkeyAbytes,33), n += 33; - hex[n++] = 33, memcpy(&hex[n],pubkeyBbytes,33), n += 33; - hex[n++] = SCRIPT_OP_2; - hex[n++] = SCRIPT_OP_CHECKMULTISIG; - hex[n++] = SCRIPT_OP_ELSE; - hex[n++] = 4; - hex[n++] = locktime & 0xff, locktime >>= 8; - hex[n++] = locktime & 0xff, locktime >>= 8; - hex[n++] = locktime & 0xff, locktime >>= 8; - hex[n++] = locktime & 0xff; - hex[n++] = SCRIPT_OP_CHECKLOCKTIMEVERIFY; - hex[n++] = SCRIPT_OP_DROP; - hex[n++] = 33; memcpy(&hex[n],pubkeyAbytes,33); n += 33; - hex[n++] = SCRIPT_OP_CHECKSIG; - hex[n++] = SCRIPT_OP_ENDIF; - for (i=0; i>4) & 0xf); - redeemscript[i*2 + 1] = hexbyte(hex[i] & 0xf); - } - redeemscript[n*2] = 0; - /*tmpbuf[0] = SCRIPT_OP_HASH160; - tmpbuf[1] = 20; - calc_OP_HASH160(scriptPubKey,tmpbuf+2,redeemscript); - tmpbuf[22] = SCRIPT_OP_EQUAL; - init_hexbytes_noT(scriptPubKey,tmpbuf,23); - if ( p2shaddr != 0 ) - { - p2shaddr[0] = 0; - if ( (btc_addr= base58_encode_check(addrtype,true,tmpbuf+2,20)) != 0 ) - { - if ( strlen(btc_addr->str) < 36 ) - strcpy(p2shaddr,btc_addr->str); - cstr_free(btc_addr,true); - } - }*/ - return(n); -} - -int32_t subatomic_approved(struct msginfo *mp,cJSON *approval,cJSON *msgjson,char *senderpub) -{ - char *hexstr,numstr[32],redeemscript[1024],*coin,*acname=""; cJSON *retjson,*decodejson; int32_t i,retval = 0; - subatomic_extrafields(approval,msgjson); - if ( mp->OTCmode == 0 ) - { - coin = (mp->bobflag != 0) ? mp->base.coin : mp->rel.coin; // the other side gets this coin - if ( strcmp(coin,"KMD") != 0 ) - { - acname = coin; - coin = ""; - } - if ( get_createmultisig2(coin,acname,mp->msigaddr,mp->redeemscript,mp->alice.secp,mp->bob.secp) != 0 ) - { - subatomic_redeemscript(redeemscript,mp->locktime,mp->alice.secp,mp->bob.secp); - if ( (decodejson= get_decodescript(coin,acname,redeemscript)) != 0 ) - { - fprintf(stderr,"%s %s msigaddr.%s %s -> %s %s\n",mp->bobflag!=0?"bob":"alice",(mp->bobflag != 0) ? mp->base.coin : mp->rel.coin,mp->msigaddr,mp->redeemscript,redeemscript,jprint(decodejson,0)); - free(decodejson); - } - } - } - sprintf(numstr,"%u",mp->origid); - for (i=0; numstr[i]!=0; i++) - sprintf(&mp->approval[i<<1],"%02x",numstr[i]); - sprintf(&mp->approval[i<<1],"%02x",' '); - i++; - mp->approval[i<<1] = 0; - jaddstr(approval,"approval",mp->approval); - hexstr = subatomic_submit(approval,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"approved",senderpub,"","")) != 0 ) - { - if ( (mp->approvalid= juint(retjson,"id")) != 0 ) - retval = 1; - printf("%u approvalid.%u (%s)\n",mp->origid,mp->approvalid,senderpub); - subatomic_status(mp,SUBATOMIC_APPROVED); - free_json(retjson); - } - free(hexstr); - return(retval); -} - -int32_t subatomic_opened(struct msginfo *mp,cJSON *opened,cJSON *msgjson,char *senderpub) -{ - char *hexstr,channelstr[65]; cJSON *retjson; int32_t retval = 0; - subatomic_extrafields(opened,msgjson); - jaddstr(opened,"opened",randhashstr(channelstr)); - hexstr = subatomic_submit(opened,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"opened",senderpub,"","")) != 0 ) - { - if ( (mp->openedid= juint(retjson,"id")) != 0 ) - retval = 1; - printf("%u openedid.%u\n",mp->origid,mp->openedid); - subatomic_status(mp,SUBATOMIC_OPENED); - free_json(retjson); - } - free(hexstr); - return(retval); -} - -int32_t subatomic_payment(struct msginfo *mp,cJSON *payment,cJSON *msgjson,char *senderpub) -{ - bits256 txid; uint64_t paytoshis; cJSON *retjson; char numstr[32],*coin,*dest,*hexstr; int32_t retval = 0; - if ( mp->bobflag == 0 ) - { - coin = mp->rel.name; - paytoshis = mp->rel.satoshis; - if ( subatomic_zonly(&mp->rel) != 0 ) - dest = mp->bob.recvZaddr; - else dest = mp->bob.recvaddr; - sprintf(numstr,"%llu",(long long)paytoshis); - jaddstr(payment,"alicepays",numstr); - jaddstr(payment,"bobdestaddr",dest); - txid = subatomic_coinpayment(mp->origid,mp->OTCmode,&mp->rel,dest,paytoshis,mp->approval,mp->bob.secp,senderpub); - jaddbits256(payment,"alicepayment",txid); - mp->alicepayment = txid; - hexstr = 0; // get it from rawtransaction of txid - jaddstr(payment,"alicetx",hexstr); - } - else - { - coin = mp->base.name; - paytoshis = mp->base.satoshis; - if ( subatomic_zonly(&mp->base) != 0 ) - dest = mp->alice.recvZaddr; - else dest = mp->alice.recvaddr; - sprintf(numstr,"%llu",(long long)paytoshis); - jaddstr(payment,"bobpays",numstr); - jaddstr(payment,"alicedestaddr",dest); - txid = subatomic_coinpayment(mp->origid,mp->OTCmode,&mp->base,dest,paytoshis,mp->approval,mp->alice.secp,senderpub); - jaddbits256(payment,"bobpayment",txid); - mp->bobpayment = txid; - hexstr = 0; // get it from rawtransaction of txid - jaddstr(payment,"bobtx",hexstr); - } - hexstr = subatomic_submit(payment,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"payment",senderpub,"","")) != 0 ) - { - if ( (mp->paymentids[0]= juint(retjson,"id")) != 0 ) - retval = 1; - printf("%u: %.8f %s -> %s, paymentid[0] %u\n",mp->origid,dstr(paytoshis),coin,dest,mp->paymentids[0]); - subatomic_status(mp,SUBATOMIC_PAYMENT); - free_json(retjson); - } - free(hexstr); - return(retval); -} - -int32_t subatomic_paidinfull(struct msginfo *mp,cJSON *paid,cJSON *msgjson,char *senderpub) -{ - char *hexstr; cJSON *retjson; int32_t retval = 0; - jaddstr(paid,"paid","in full"); - subatomic_extrafields(paid,msgjson); - hexstr = subatomic_submit(paid,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"paid",senderpub,"","")) != 0 ) - { - if ( (mp->paidid= juint(retjson,"id")) != 0 ) - retval = 1; - printf("%u paidid.%u\n",mp->origid,mp->paidid); - subatomic_status(mp,SUBATOMIC_PAIDINFULL); - free_json(retjson); - } - free(hexstr); - return(retval); -} - -int32_t subatomic_closed(struct msginfo *mp,cJSON *closed,cJSON *msgjson,char *senderpub) -{ - char *hexstr; cJSON *retjson; int32_t retval = 0; - jaddnum(closed,"closed",mp->origid); - subatomic_extrafields(closed,msgjson); - hexstr = subatomic_submit(closed,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"closed",senderpub,"","")) != 0 ) - { - if ( (mp->closedid= juint(retjson,"id")) != 0 ) - retval = 1; - subatomic_status(mp,SUBATOMIC_CLOSED); - printf("%u closedid.%u\n",mp->origid,mp->closedid); - free_json(retjson); - } - free(hexstr); - return(retval); -} - -uint32_t subatomic_alice_openrequest(struct msginfo *origmp) -{ - struct msginfo *mp; cJSON *retjson,*openrequest; char *hexstr,*str,tmpstr[32]; - mp = subatomic_tracker(origmp->origid); - mp->origid = origmp->origid; - mp->rel.satoshis = origmp->rel.satoshis; - mp->rel.istoken = origmp->rel.istoken; - snprintf(mp->rel.tokenid,strlen(origmp->rel.tokenid) + 1,"%s",origmp->rel.tokenid); - snprintf(mp->rel.name,strlen(origmp->rel.name) + 1,"%s",origmp->rel.name); - snprintf(mp->rel.coin,strlen(subatomic_checkname(tmpstr,mp,1,origmp->rel.name)) + 1,"%s",subatomic_checkname(tmpstr,mp,1,origmp->rel.name)); - snprintf(mp->alice.pubkey,strlen(DPOW_pubkeystr) + 1,"%s",DPOW_pubkeystr); - snprintf(mp->alice.secp,strlen(DPOW_secpkeystr) + 1,"%s",DPOW_secpkeystr); - snprintf(mp->alice.recvZaddr,strlen(DPOW_recvZaddr) + 1,"%s",DPOW_recvZaddr); - snprintf(mp->alice.recvaddr,strlen(DPOW_recvaddr) + 1,"%s",DPOW_recvaddr); - printf("rel.%s/%s %s openrequest %u status.%d (%s/%s)\n",mp->rel.name,mp->rel.coin,mp->rel.tokenid,mp->origid,mp->status,mp->alice.recvaddr,mp->alice.recvZaddr); - if ( mp->status == 0 && subatomic_orderbook_mpset(mp,"") != 0 ) - { - snprintf(mp->bob.pubkey,strlen(mp->senderpub) + 1,"%s",mp->senderpub); - if ( subatomic_zonly(&mp->base) != 0 || subatomic_zonly(&mp->rel) != 0 ) - mp->OTCmode = 1; - else mp->OTCmode = SUBATOMIC_OTCDEFAULT; - snprintf(origmp->base.name,strlen(mp->base.name) + 1,"%s",mp->base.name); - snprintf(origmp->base.coin,strlen(mp->base.coin) + 1,"%s",mp->base.coin); - origmp->base.istoken = mp->base.istoken; - snprintf(origmp->base.tokenid,strlen(mp->base.tokenid) + 1,"%s",mp->base.tokenid); - origmp->OTCmode = mp->OTCmode; - if ( mp->rel.istoken != 0 && ((mp->rel.satoshis % SATOSHIDEN) != 0 || mp->rel.iszaddr != 0) ) - { - printf("%u cant do zaddr or fractional rel %s.%s tokens %.8f\n",mp->origid,mp->rel.coin,mp->rel.tokenid,dstr(mp->rel.satoshis)); - return(0); - } - else if ( mp->base.istoken != 0 && ((mp->base.satoshis % SATOSHIDEN) != 0 || mp->base.iszaddr != 0 ) ) - { - printf("%u cant do zaddr or fractional base %s.%s tokens %.8f\n",mp->origid,mp->base.coin,mp->base.tokenid,dstr(mp->base.satoshis)); - return(0); - } - else if ( (openrequest= subatomic_mpjson(mp)) != 0 ) - { - hexstr = subatomic_submit(openrequest,!mp->bobflag); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"openrequest",mp->bob.pubkey,"","")) != 0 ) - { - mp->openrequestid = juint(retjson,"id"); - printf("%u openrequest.%u -> (%s)\n",mp->origid,mp->openrequestid,mp->bob.pubkey); - subatomic_status(mp,SUBATOMIC_OPENREQUEST); - free_json(retjson); - } - free(hexstr); - } - } - return(mp->openrequestid); -} - -void subatomic_bob_gotopenrequest(uint32_t inboxid,char *senderpub,cJSON *msgjson,char *basename,char *relname) -{ - struct msginfo *mp; cJSON *approval; int32_t origid; char *addr,tmpstr[32],*coin,*acname=""; - origid = juint(msgjson,"origid"); - mp = subatomic_tracker(origid); - snprintf(mp->base.name,strlen(basename) + 1,"%s",basename); - snprintf(mp->base.coin,strlen(subatomic_checkname(tmpstr,mp,0,basename)) + 1,"%s",subatomic_checkname(tmpstr,mp,0,basename)); - snprintf(mp->rel.name,strlen(relname) + 1,"%s",relname); - snprintf(mp->rel.coin,strlen(subatomic_checkname(tmpstr,mp,1,relname)) + 1,"%s",subatomic_checkname(tmpstr,mp,1,relname)); - mp->origid = origid; - mp->rel.satoshis = j64bits(msgjson,"relsatoshis"); - mp->bobflag = 1; - snprintf(mp->bob.pubkey,strlen(DPOW_pubkeystr) + 1,"%s",DPOW_pubkeystr); - snprintf(mp->bob.secp,strlen(DPOW_secpkeystr) + 1,"%s",DPOW_secpkeystr); - snprintf(mp->bob.recvZaddr,strlen(DPOW_recvZaddr) + 1,"%s",DPOW_recvZaddr); - snprintf(mp->bob.recvaddr,strlen(DPOW_recvaddr) + 1,"%s",DPOW_recvaddr); - if ( (addr= jstr(msgjson,"aliceaddr")) != 0 ) - snprintf(mp->alice.recvaddr,strlen(addr) + 1,"%s",addr); - if ( (addr= jstr(msgjson,"aliceZaddr")) != 0 ) - snprintf(mp->alice.recvZaddr,strlen(addr) + 1,"%s",addr); - if ( (addr= jstr(msgjson,"alicesecp")) != 0 ) - snprintf(mp->alice.secp,strlen(addr) + 1,"%s",addr); - if ( subatomic_zonly(&mp->base) != 0 || subatomic_zonly(&mp->rel) != 0 ) - mp->OTCmode = 1; - else mp->OTCmode = SUBATOMIC_OTCDEFAULT; - printf("%u got open request\n",mp->origid); - if ( mp->status == 0 && subatomic_orderbook_mpset(mp,basename) != 0 && (approval= subatomic_mpjson(mp)) != 0 ) - { - if ( mp->rel.istoken != 0 && ((mp->rel.satoshis % SATOSHIDEN) != 0 || mp->rel.iszaddr != 0) ) - { - printf("%u cant do zaddr or fractional rel %s.%s tokens %.8f\n",mp->origid,mp->rel.coin,mp->rel.tokenid,dstr(mp->rel.satoshis)); - subatomic_closed(mp,approval,msgjson,senderpub); - return; - } - else if ( mp->base.istoken != 0 && ((mp->base.satoshis % SATOSHIDEN) != 0 || mp->base.iszaddr != 0 ) ) - { - printf("%u cant do zaddr or fractional base %s.%s tokens %.8f\n",mp->origid,mp->base.coin,mp->base.tokenid,dstr(mp->base.satoshis)); - subatomic_closed(mp,approval,msgjson,senderpub); - return; - } - else if ( subatomic_getbalance(&mp->base) < mp->base.satoshis ) - { - printf("%u bob node low on %s funds! %.8f not enough for %.8f\n",mp->origid,mp->base.coin,dstr(subatomic_getbalance(&mp->base)),dstr(mp->base.satoshis)); - subatomic_closed(mp,approval,msgjson,senderpub); - } - else - { - printf("%u bob (%s/%s) gotopenrequest origid.%u status.%d (%s/%s) SENDERPUB.(%s)\n",mp->origid,mp->base.name,mp->rel.name,mp->origid,mp->status,mp->bob.recvaddr,mp->bob.recvZaddr,senderpub); - subatomic_approved(mp,approval,msgjson,senderpub); - } - } -} - -int32_t subatomic_channelapproved(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) -{ - struct msginfo *mp; cJSON *approval; char *addr,*coin,*acname; int32_t retval = 0; - mp = subatomic_tracker(juint(msgjson,"origid")); - if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (approval= subatomic_mpjson(mp)) != 0 ) - { - printf("%u iambob.%d (%s/%s) channelapproved origid.%u status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->origid,mp->status); - if ( mp->bobflag == 0 && mp->status == SUBATOMIC_OPENREQUEST ) - { - if ( (addr= jstr(msgjson,"bobaddr")) != 0 ) - snprintf(mp->bob.recvaddr,strlen(addr) + 1,"%s",addr); - if ( (addr= jstr(msgjson,"bobZaddr")) != 0 ) - snprintf(mp->bob.recvZaddr,strlen(addr) + 1,"%s",addr); - if ( (addr= jstr(msgjson,"bobsecp")) != 0 ) - snprintf(mp->bob.secp,strlen(addr) + 1,"%s",addr); - retval = subatomic_approved(mp,approval,msgjson,senderpub); - } - else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_APPROVED ) - retval = subatomic_opened(mp,approval,msgjson,senderpub); - } - return(retval); -} - -int32_t subatomic_incomingopened(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) -{ - struct msginfo *mp; cJSON *payment; int32_t retval = 0; - mp = subatomic_tracker(juint(msgjson,"origid")); - if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (payment= subatomic_mpjson(mp)) != 0 ) - { - printf("%u iambob.%d (%s/%s) incomingchannel status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); - if ( mp->bobflag == 0 && mp->status == SUBATOMIC_APPROVED ) - retval = subatomic_payment(mp,payment,msgjson,senderpub); - else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_OPENED ) - retval = 1; // nothing to do - } - return(retval); -} - -int32_t subatomic_incomingpayment(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) -{ - static FILE *fp; - struct msginfo *mp; cJSON *pay,*rawtx,*retjson; bits256 txid; char str[65],*hexstr; int32_t retval = 0; - mp = subatomic_tracker(juint(msgjson,"origid")); - if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (pay= subatomic_mpjson(mp)) != 0 ) - { - printf("%u iambob.%d (%s/%s) incomingpayment status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); - if ( mp->bobflag == 0 ) - { - txid = jbits256(msgjson,"bobpayment"); - jaddbits256(msgjson,"alicepayment",mp->alicepayment); - printf("%u alice waits for %s.%s to be in mempool (%.8f -> %s)\n",mp->origid,mp->base.name,bits256_str(str,txid),dstr(mp->base.satoshis),subatomic_zonly(&mp->base) == 0 ? mp->alice.recvaddr : mp->alice.recvZaddr); - hexstr = jstr(msgjson,"bobtx"); - if ( (rawtx= subatomic_txidwait(&mp->base,txid,hexstr,SUBATOMIC_TIMEOUT,senderpub)) != 0 ) - { - if ( subatomic_verifypayment(&mp->base,rawtx,mp->base.satoshis,subatomic_zonly(&mp->base) == 0 ? mp->alice.recvaddr : mp->alice.recvZaddr,txid) >= 0 ) - mp->gotpayment = 1; - free_json(rawtx); - } - if ( mp->gotpayment != 0 ) - { - printf("%u SWAP COMPLETE <<<<<<<<<<<<<<<<\n",mp->origid); - SUBATOMIC_retval = 0; - if ( mp->base.iszaddr == 0 ) - { - sprintf(str,"%u",mp->origid); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,bits256_str(str,mp->alicepayment),(char *)"completed",str,DPOW_pubkeystr,"","")) != 0 ) - free_json(retjson); - } - } - else - { - printf("%u SWAP INCOMPLETE, waiting on %s.%s\n",mp->origid,mp->base.name,bits256_str(str,txid)); - if ( (fp= fopen("SUBATOMIC.incomplete","a+")) != 0 ) - { - char *jsonstr = jprint(msgjson,0); - fwrite(jsonstr,1,strlen(jsonstr),fp); - fputc('\n',fp); - fclose(fp); - free(jsonstr); - } - if ( mp->base.iszaddr == 0 ) - { - sprintf(str,"%u",mp->origid); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,bits256_str(str,mp->alicepayment),(char *)"incomplete",str,DPOW_pubkeystr,"","")) != 0 ) - free_json(retjson); - } - subatomic_closed(mp,pay,msgjson,senderpub); - exit(-1); - } - } - if ( mp->gotpayment != 0 ) - retval = subatomic_paidinfull(mp,pay,msgjson,senderpub); - else - { - if ( mp->bobflag != 0 && mp->status == SUBATOMIC_OPENED ) - { - txid = jbits256(msgjson,"alicepayment"); - printf("%u bob waits for %s.%s to be in mempool (%.8f -> %s)\n",mp->origid,mp->rel.name,bits256_str(str,txid),dstr(mp->rel.satoshis),subatomic_zonly(&mp->rel) == 0 ? mp->bob.recvaddr : mp->bob.recvZaddr); - hexstr = jstr(msgjson,"alicetx"); - if ( (rawtx= subatomic_txidwait(&mp->rel,txid,hexstr,SUBATOMIC_TIMEOUT,senderpub)) != 0 ) - { - if ( subatomic_verifypayment(&mp->rel,rawtx,mp->rel.satoshis,subatomic_zonly(&mp->rel) == 0 ? mp->bob.recvaddr : mp->bob.recvZaddr,txid) >= 0 ) - mp->gotpayment = 1; - free_json(rawtx); - } - if ( mp->gotpayment != 0 ) - { - retval = subatomic_payment(mp,pay,msgjson,senderpub); - jaddbits256(msgjson,"bobpayment",mp->bobpayment); - if ( mp->rel.iszaddr == 0 ) - { - sprintf(str,"%u",mp->origid); - if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,bits256_str(str,mp->bobpayment),(char *)"completed",str,DPOW_pubkeystr,"","")) != 0 ) - free_json(retjson); - } - printf("%u SWAP COMPLETE <<<<<<<<<<<<<<<<\n",mp->origid); - if ( (fp= fopen("SUBATOMIC.proof","rb+")) == 0 ) - fp = fopen("SUBATOMIC.proof","wb"); - if ( fp != 0 ) - { - char *jsonstr = jprint(msgjson,0); - fseek(fp,0,SEEK_END); - fwrite(jsonstr,1,strlen(jsonstr),fp); - fputc('\n',fp); - fflush(fp); - free(jsonstr); - } - } else printf("%u SWAP INCOMPLETE: %s\n",mp->origid,jprint(msgjson,0)); - } - } - } - return(retval); -} - -int32_t subatomic_incomingfullypaid(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) -{ - struct msginfo *mp; cJSON *closed; int32_t retval = 0; - mp = subatomic_tracker(juint(msgjson,"origid")); - if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (closed= subatomic_mpjson(mp)) != 0 ) - { - printf("%u iambob.%d (%s/%s) incomingfullypaid status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); - // error check msgjson vs M - if ( mp->bobflag == 0 && mp->status == SUBATOMIC_PAIDINFULL ) - retval = subatomic_closed(mp,closed,msgjson,senderpub); - else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_PAYMENT ) - retval = subatomic_paidinfull(mp,closed,msgjson,senderpub); - } - return(retval); -} - -int32_t subatomic_incomingclosed(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) -{ - struct msginfo *mp; cJSON *closed; int32_t retval = 0; - mp = subatomic_tracker(juint(msgjson,"origid")); - if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (closed= subatomic_mpjson(mp)) != 0 ) - { - printf("%u iambob.%d (%s/%s) incomingclose status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); - if ( mp->bobflag != 0 ) - dpow_cancel(mp->origid); - if ( mp->status < SUBATOMIC_CLOSED ) - { - retval = subatomic_closed(mp,closed,msgjson,senderpub); - subatomic_status(mp,SUBATOMIC_CLOSED); - } - retval = 1; - } - return(retval); -} - -int32_t subatomic_ismine(int32_t bobflag,cJSON *json,char *basename,char *relname) -{ - char *base,*rel; - if ( (base= jstr(json,"base")) != 0 && (rel= jstr(json,"rel")) != 0 ) - { - if ( strcmp(base,basename) == 0 && strcmp(rel,relname) == 0 ) - return(1); - if ( bobflag != 0 ) - { - if ( strcmp(basename,"#allfiles") == 0 && base[0] == '#' ) - return(1); - fprintf(stderr,"skip ismine (%s/%s) vs (%s/%s)\n",basename,relname,base,rel); - } - } - return(0); -} - -void subatomic_tokensregister(int32_t priority) -{ - char *token_name,*tokenid,existing[65]; cJSON *tokens,*token; int32_t i,numtokens; - if ( SUBATOMIC_json != 0 && (tokens= jarray(&numtokens,SUBATOMIC_json,"tokens")) != 0 ) - { - for (i=0; i 0 ) - { - for (j=0; j %s, %u %llu %u\n",mp->bobflag,mp->base.name,mp->rel.name,mp->origid,(long long)mp->rel.satoshis,mp->openrequestid); - while ( 1 ) - { - if ( msgs == 0 ) - { - sleep(1); - fflush(stdout); - if ( mp->bobflag != 0 ) - { - dpow_pubkeyregister(SUBATOMIC_PRIORITY); - subatomic_tokensregister(SUBATOMIC_PRIORITY); - subatomic_filesregister(SUBATOMIC_PRIORITY); - } - } - msgs = 0; - for (iter=0; iter<(int32_t)(sizeof(tagBs)/sizeof(*tagBs)); iter++) - { - tagB = tagBs[iter]; - if ( (ptrs= dpow_inboxcheck(&n,&stopats[iter],tagB)) != 0 ) - { - for (i=0; ijsonstr)) != 0 ) - { - if ( jint(inboxjson,"tobob") != mp->bobflag ) - continue; - if ( subatomic_ismine(mp->bobflag,inboxjson,mp->base.name,mp->rel.name) != 0 ) - { - if ( strcmp(tagB,"openrequest") == 0 && mp->bobflag != 0 ) - subatomic_bob_gotopenrequest(ptr->shorthash,ptr->senderpub,inboxjson,mp->base.name,mp->rel.name); - else if ( strcmp(tagB,"approved") == 0 ) - mask |= subatomic_channelapproved(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 0; - else if ( strcmp(tagB,"opened") == 0 ) - mask |= subatomic_incomingopened(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 1; - else if ( strcmp(tagB,"payment") == 0 ) - mask |= subatomic_incomingpayment(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 2; - else if ( strcmp(tagB,"paid") == 0 ) - mask |= subatomic_incomingfullypaid(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 3; - else if ( strcmp(tagB,"closed") == 0 ) - mask |= subatomic_incomingclosed(ptr->shorthash,ptr->senderpub,inboxjson,mp) * 0x1f; - else fprintf(stderr,"iambob.%d unknown unexpected tagB.(%s)\n",mp->bobflag,tagB); - } - free_json(inboxjson); - } else fprintf(stderr,"subatomic iambob.%d loop got unparseable(%s)\n",mp->bobflag,ptr->jsonstr); - free(ptr); - ptrs[i] = 0; - } - } - free(ptrs); - } - } - if ( mp->bobflag == 0 && (mask & 0x1f) == 0x1f ) - { - printf("alice %u %llu %u finished\n",mp->origid,(long long)mp->rel.satoshis,mp->openrequestid); - break; - } - } -} - -int32_t main(int32_t argc,char **argv) -{ - char *fname = "subatomic.json"; - int32_t i,height; char *coin,*kcli,*subatomic,*hashstr,*acname=(char *)""; cJSON *retjson; bits256 blockhash; char checkstr[65],str[65],str2[65],tmpstr[32]; long fsize; struct msginfo M; - memset(&M,0,sizeof(M)); - srand((int32_t)time(NULL)); - if ( (subatomic= filestr(&fsize,fname)) == 0 ) - { - fprintf(stderr,"cant load %s file\n",fname); - exit(-1); - } - if ( (SUBATOMIC_json= cJSON_Parse(subatomic)) == 0 ) - { - fprintf(stderr,"cant parse subatomic.json file (%s)\n",subatomic); - exit(-1); - } - free(subatomic); - if ( argc >= 4 ) - { - if ( dpow_pubkey() < 0 ) - { - fprintf(stderr,"couldnt set pubkey for DEX\n"); - return(-1); - } - coin = (char *)argv[1]; - if ( argv[2][0] != 0 ) - REFCOIN_CLI = (char *)argv[2]; - else - { - if ( strcmp(coin,"KMD") != 0 ) - { - acname = coin; - } - } - hashstr = (char *)argv[3]; - snprintf(M.rel.coin,strlen(subatomic_checkname(tmpstr,&M,1,coin)) + 1,"%s",subatomic_checkname(tmpstr,&M,1,coin)); - snprintf(M.rel.name,strlen(coin) + 1,"%s",coin); - if ( argc == 4 && strlen(hashstr) == 64 ) // for blocknotify usage, seems not needed - { - height = get_coinheight(coin,acname,&blockhash); - bits256_str(checkstr,blockhash); - if ( strcmp(checkstr,hashstr) == 0 ) - { - fprintf(stderr,"%s: (%s) %s height.%d\n",coin,REFCOIN_CLI!=0?REFCOIN_CLI:"",checkstr,height); - if ( (retjson= dpow_ntzdata(coin,SUBATOMIC_PRIORITY,height,blockhash)) != 0 ) - free_json(retjson); - } else fprintf(stderr,"coin.%s (%s) %s vs %s, height.%d\n",coin,REFCOIN_CLI!=0?REFCOIN_CLI:"",checkstr,hashstr,height); - if ( strcmp("BTC",coin) != 0 ) - { - bits256 prevntzhash,ntzhash; int32_t prevntzheight,ntzheight; uint32_t ntztime,prevntztime; char hexstr[81]; cJSON *retjson2; - prevntzhash = dpow_ntzhash(coin,&prevntzheight,&prevntztime); - if ( (retjson= get_getinfo(coin,acname)) != 0 ) - { - ntzheight = juint(retjson,"notarized"); - ntzhash = jbits256(retjson,"notarizedhash"); - if ( ntzheight > prevntzheight ) - { - get_coinmerkleroot(coin,acname,ntzhash,&ntztime); - fprintf(stderr,"NOTARIZATION %s.%d %s t.%u\n",coin,ntzheight,bits256_str(str,ntzhash),ntztime); - bits256_str(hexstr,ntzhash); - sprintf(&hexstr[64],"%08x",ntzheight); - sprintf(&hexstr[72],"%08x",ntztime); - hexstr[80] = 0; - if ( (retjson2= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,coin,"notarizations",DPOW_pubkeystr,"","")) != 0 ) - free_json(retjson2); - } - else if ( ntzheight == prevntzheight && memcmp(&prevntzhash,&ntzhash,32) != 0 ) - fprintf(stderr,"NTZ ERROR %s.%d %s != %s\n",coin,ntzheight,bits256_str(str,ntzhash),bits256_str(str2,prevntzhash)); - free_json(retjson); - } - } - } - else if ( argc == 5 && atol(hashstr) > 10000 ) - { - char checkstr[32]; uint64_t mult = 1; - M.origid = (uint32_t)atol(hashstr); - sprintf(checkstr,"%u",M.origid); - if ( strcmp(checkstr,hashstr) == 0 ) // alice - { - M.rel.satoshis = (uint64_t)(atof(argv[4])*SATOSHIDEN+0.0000000049999); - for (i=0; M.rel.name[i]!=0; i++) - if ( M.rel.name[i] == '.' ) - { - mult = SATOSHIDEN; - break; - } - if ( subatomic_getbalance(&M.rel) < M.rel.satoshis/mult ) - { - fprintf(stderr,"not enough balance %s %.8f for %.8f\n",M.rel.coin,dstr(subatomic_getbalance(&M.rel)),dstr(M.rel.satoshis/mult)); - return(-1); - } - fprintf(stderr,"subatomic_channel_alice (%s/%s) %s %u with %.8f %llu\n",M.rel.name,M.rel.coin,hashstr,M.origid,atof(argv[4]),(long long)M.rel.satoshis); - dpow_pubkeyregister(SUBATOMIC_PRIORITY); - M.openrequestid = subatomic_alice_openrequest(&M); - if ( M.openrequestid != 0 ) - subatomic_loop(&M); - } else fprintf(stderr,"checkstr mismatch %s %s != %s\n",coin,hashstr,checkstr); - } - else - { - M.bobflag = 1; - snprintf(M.base.name,strlen(hashstr) + 1,"%s",hashstr); - snprintf(M.base.coin,strlen(subatomic_checkname(tmpstr,&M,0,hashstr)) + 1,"%s",subatomic_checkname(tmpstr,&M,0,hashstr)); - subatomic_loop(&M); // while ( 1 ) loop for each relcoin -> basecoin - } - } - return(SUBATOMIC_retval); -} - diff --git a/src/cc/dapps/subatomic.json b/src/cc/dapps/subatomic.json deleted file mode 100644 index 7420c712cd5..00000000000 --- a/src/cc/dapps/subatomic.json +++ /dev/null @@ -1,26 +0,0 @@ -{ -"authorized": [ - {"chmex":"030754bffcf6dfcb34a20c486ff5a5be5546b9cc16fba9692165272b3f8e98c4af" }, - {"SHossain":"03c8657bd57b6ceb14514a10e99fe8a0cec5a9bc24592df7f66f050e670e4f6bac" }, - {"satinder":"03732f8ef851ff234c74d0df575c2c5b159e2bab3faca4ec52b3f217d5cda5361d" }, - {"ml777":"02453d028c74cb9551e1aaf35113383b6ecbd9f06ff23a4ab1a953429b9763e345" }, - {"tonylhub":"0218e0f435d4544404c25a7759b7f7174d821215085ef936218c5569d975af468b" }, - {"gthub":"036c7de9a5090fbad78b9eea41549ccacc07bd0e9e7f8d290c88f470f3569e1a35" }, - {"zkTrader":"026c6b0b35ec0adc2f8a5c648da1fce634f798c69d5e9fe518400447e88398b830" }, - {"nutellalicka":"03aee08860e0340f0f490a3ef3718d6676882f2d63d4f536dfebb1d348b82c79ee" }, - {"gcharang":"02d3431950c2f0f9654217b6ce3d44468d3a9ca7255741767fdeee7c5ec6b47567" }, - {"jl777":"02b27de3ee5335518b06f69f4fbabb029cfc737613b100996841d5532b324a5a61" } -], -"tokens":[ - {"RICK.demo":"2b1feef719ecb526b07416dd432bce603ac6dc8bfe794cddf105cb52f6aae3cd"} -], -"files":[ - {"filename":"komodod","prices":[{"KMD":0.1}, {"PIRATE":1}]} -], -"externalcoins":[ - { "BTC":"bitcoin-cli" }, - { "CHIPS":"chips-cli" }, - { "VRSC":"verus" } -] -} - diff --git a/src/cc/dapps/subatomic_utils.cpp b/src/cc/dapps/subatomic_utils.cpp deleted file mode 100644 index 8f22ad382df..00000000000 --- a/src/cc/dapps/subatomic_utils.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include "subatomic_utils.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace fs = std::filesystem; - -using t_config_key = std::string; -using t_config_value = std::string; -using t_config_registry = std::unordered_map; -using t_coin_config_registry = std::unordered_map; -static t_config_registry conf; // conf["rpcuser"] -static t_coin_config_registry conf_coins; // conf_coins["SEC"]["rpcuser"], - -char* construct_json(const char* method, size_t count, ...) -{ - std::string json; - json += "{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\":\""; - json += method; - json += "\", \"params\": ["; - va_list ap; - va_start(ap, count); - for (size_t j = 0; j < count; j++) { - auto str = std::string(va_arg(ap, char *)); - if (str.empty()) continue; - json += "\""; - json += str; - json += "\""; - if (j + 1 < count) { - json += ","; - } - } - va_end(ap); - json += "] }"; - std::cout << "json: " << json << std::endl; - return strdup(json.c_str()); -} -char* concatenate(const char* s1, const char* s2) -{ - return strdup((std::string(s1) + std::string(s2)).c_str()); -} - -int count_uppers(const std::string& s) -{ - return std::count_if(s.begin(), s.end(), - [](unsigned char c){ return std::isupper(c); } // correct - ); -} - -void dump_cfg() { - for (auto&& [key, value] : conf) { - std::cout << key << " " << value << std::endl; - } - - for (auto&& [key, cfg] : conf_coins) { - std::cout << "--------------------------------------" << std::endl; - std::cout << "cfg: " << key << std::endl; - for (auto&& [key, value] : cfg) { - std::cout << key << " " << value << std::endl; - } - } -} - -char* get_komodo_config_path() -{ - #ifdef _WIN32 - fs::path conf_root = fs::path(std::getenv("APPDATA")) / ".komodo/komodo.conf"; - #else - fs::path conf_root = fs::path(std::getenv("HOME")) / ".komodo/komodo.conf"; - #endif - return strdup(conf_root.c_str()); -} - -char* get_komodo_config_parent_path() -{ - #ifdef _WIN32 - fs::path conf_root = fs::path(std::getenv("APPDATA")) / ".komodo"; - #else - fs::path conf_root = fs::path(std::getenv("HOME")) / ".komodo"; - #endif - return strdup(conf_root.c_str()); -} - -bool get_conf_content(const std::string& ticker, std::vector& vec_out) -{ - char* path = nullptr; - if (ticker == "KMD") { - path = get_komodo_config_path(); - } else { - path = get_komodo_config_parent_path(); - auto tmp = fs::path(path) / ticker / (ticker + ".conf"); - free(path); - path = strdup(tmp.c_str()); - std::cout << "ticker conf path: " << path << std::endl; - } - std::ifstream ifs(path); - if (!ifs) { - return false; - } - std::string line; - while (std::getline(ifs, line)) - { - if(line.size() > 0) - vec_out.push_back(line); - } - free(path); - return true; -} - -bool is_komodo_config_filled() -{ - return not conf.empty(); -} - -bool fill_config() { - auto fill_functor = [](auto&& out_map, auto&& out_lines, const std::string& ticker = "") { - t_config_registry out; - for (auto&& cur: out_lines) { - //! Split left / right side - if (cur.find("=") != std::string::npos) { - auto key = cur.substr(0, cur.find("=")); - auto value = cur.substr(cur.find("=") + 1); - out[key] = value; - } - } - using out_t = std::decay_t; - if constexpr(std::is_same_v) { - out_map = out; - } else { - out_map[ticker] = out; - } - }; - if (is_komodo_config_filled()) { - return true; - } - std::vector out; - if (not get_conf_content("KMD", out)) { - return false; - } - - fill_functor(conf, out); - //! komodo is filled - out.clear(); - auto parent_p = get_komodo_config_parent_path(); - for (auto&& cur_dir : fs::directory_iterator(parent_p)) { - auto ticker = cur_dir.path().filename().string(); - if (fs::is_directory(cur_dir.path()) && fs::exists(fs::path(parent_p) / ticker / (ticker + ".conf"))) { - get_conf_content(ticker, out); - fill_functor(conf_coins, out, ticker); - out.clear(); - std::cout << cur_dir.path().filename().c_str() << std::endl; - } - } - free(parent_p); - return true; -} - -const char* get_rpcport(const char* ticker) -{ - if (strcmp(ticker, "KMD") == 0) { - if (conf.find("rpcport") != conf.end()) { - return conf["rpcport"].c_str(); - } else { - return "7776"; - } - } - return conf_coins[ticker]["rpcport"].c_str(); -} - -const char* get_rpcuser(const char* ticker) { - if (strcmp(ticker, "KMD") == 0) { - return conf["rpcuser"].c_str(); - } else { - return conf_coins[ticker]["rpcuser"].c_str(); - } -} - -const char* get_rpcpassword(const char* ticker) -{ - if (strcmp(ticker, "KMD") == 0) { - return conf["rpcpassword"].c_str(); - } else { - return conf_coins[ticker]["rpcpassword"].c_str(); - } -} diff --git a/src/cc/dapps/subatomic_utils.h b/src/cc/dapps/subatomic_utils.h deleted file mode 100644 index 4fdde37fd4a..00000000000 --- a/src/cc/dapps/subatomic_utils.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - - char* get_komodo_config_path(); - bool is_komodo_config_filled(); - bool fill_config(); //! Fill configs - const char* get_cfg_value(const char* ticker, const char* field); //! return NULL if not found - const char* get_rpcuser(const char* ticker); - const char* get_rpcpassword(const char* ticker); - const char* get_rpcport(const char* ticker); - char* concatenate(const char* s1, const char* s2); //! need to be free - char* construct_json(const char* method, size_t count, ...); - void dump_cfg(); -#ifdef __cplusplus -} -#endif diff --git a/src/cc/dice.cpp b/src/cc/dice.cpp index b56957fbfb2..48d1049f91f 100644 --- a/src/cc/dice.cpp +++ b/src/cc/dice.cpp @@ -1226,7 +1226,7 @@ bool DicePlanExists(CScript &fundingPubKey,uint256 &fundingtxid,struct CCcontrac char CCaddr[64]; uint64_t sbits=0; uint256 txid,hashBlock; CTransaction tx; std::vector txids; GetCCaddress(cp,CCaddr,dicepk); - SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,0,zeroid,'F'); + SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,zeroid,'F'); if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan { //fprintf(stderr,"check fundingtxid\n"); @@ -1321,7 +1321,7 @@ UniValue DiceList() { UniValue result(UniValue::VARR); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; uint64_t sbits; int64_t minbet,maxbet,maxodds,timeoutblocks; char str[65]; cp = CCinit(&C,EVAL_DICE); - SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,0,zeroid,'F'); + SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,zeroid,'F'); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { txid = *it; diff --git a/src/cc/dilithium.c b/src/cc/dilithium.c index 8916ea60ea0..afeef33d941 100644 --- a/src/cc/dilithium.c +++ b/src/cc/dilithium.c @@ -3586,7 +3586,7 @@ void dilithium_handleinit(struct CCcontract_info *cp) pthread_mutex_init(&DILITHIUM_MUTEX,NULL); dilithiumpk = GetUnspendable(cp,0); GetCCaddress(cp,CCaddr,dilithiumpk); - SetCCtxids(txids,CCaddr,true,cp->evalcode,0,zeroid,'R'); + SetCCtxids(txids,CCaddr,true,cp->evalcode,zeroid,'R'); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { txid = *it; diff --git a/src/cc/eval.h b/src/cc/eval.h index 8c0606617fb..1324d5e2127 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -58,7 +58,6 @@ EVAL(EVAL_GATEWAYS, 0xf1) \ EVAL(EVAL_TOKENS, 0xf2) \ EVAL(EVAL_IMPORTGATEWAY, 0xf3) \ - EVAL(EVAL_KOGS, 0xf4) \ // evalcodes 0x10 to 0x7f are reserved for cclib dynamic CC diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 48a228fcc33..a476ef92b07 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -253,20 +253,20 @@ uint8_t games_keystrokesopretdecode(uint256 &gametxid,uint256 &batontxid,CPubKey uint8_t games_registeropretdecode(uint256 &gametxid,uint256 &tokenid,uint256 &playertxid,CScript scriptPubKey) { std::string name, description; std::vector vorigPubkey; - std::vector oprets; + std::vector> oprets; std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; - uint8_t f,*script; std::vector voutPubkeys; + uint8_t e, f,*script; std::vector voutPubkeys; tokenid = zeroid; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( script[1] == 'c' && (f= DecodeTokenCreateOpRetV1(scriptPubKey,origpubkey,name,description,oprets)) == 'c' ) + if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description,oprets)) == 'c' ) { - GetOpReturnCCBlob(oprets, vopretNonfungible); + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); vopret = vopretNonfungible; } - else if ( script[1] != 'R' && (f= DecodeTokenOpRetV1(scriptPubKey, tokenid, voutPubkeys, oprets)) != 0 ) + else if ( script[1] != 'R' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, oprets)) != 0 ) { - GetOpReturnCCBlob(oprets, vopretDummy); // blob from non-creation tx opret + GetOpretBlob(oprets, OPRETID_ROGUEGAMEDATA, vopretDummy); // blob from non-creation tx opret vopret = vopretDummy; } if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> playertxid) != 0 && e == EVAL_GAMES && f == 'R' ) @@ -287,18 +287,18 @@ CScript games_finishopret(uint8_t funcid,uint256 gametxid,int32_t regslot,CPubKe uint8_t games_finishopretdecode(uint256 &gametxid, uint256 &tokenid, int32_t ®slot, CPubKey &pk, std::vector &playerdata, std::string &symbol, std::string &pname,CScript scriptPubKey) { std::string name, description; std::vector vorigPubkey; - std::vector oprets, opretsDummy; + std::vector> oprets, opretsDummy; std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; - uint8_t f,*script; std::vector voutPubkeys; + uint8_t e, f,*script; std::vector voutPubkeys; tokenid = zeroid; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( script[1] == 'c' && (f= DecodeTokenCreateOpRetV1(scriptPubKey,origpubkey,name,description, oprets)) == 'c' ) + if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description, oprets)) == 'c' ) { - GetOpReturnCCBlob(oprets, vopretNonfungible); + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); vopret = vopretNonfungible; } - else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRetV1(scriptPubKey, tokenid, voutPubkeys, opretsDummy)) != 0 ) + else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, opretsDummy)) != 0 ) { //fprintf(stderr,"decode opret %c tokenid.%s\n",script[1],tokenid.GetHex().c_str()); GetNonfungibleData(tokenid, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx @@ -1275,15 +1275,15 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) CCaddr1of2set(cp,gamespk,gamespk,cp->CCpriv,destaddr); mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode, 1, burnpk)); - uint8_t funcid; uint256 tid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0; + uint8_t e, funcid; uint256 tid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0; CScript opretRegister = games_registeropret(gametxid, playertxid); if ( playertxid != zeroid ) { voutPubkeysEmpty.push_back(burnpk); if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 ) { - std::vector oprets; - if ( (funcid= DecodeTokenOpRetV1(playertx.vout.back().scriptPubKey, tid, voutPubkeys, oprets)) != 0) + std::vector> oprets; + if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tid, voutPubkeys, oprets)) != 0) { // if token in the opret didtx = 1; if ( funcid == 'c' ) @@ -1291,7 +1291,7 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) vscript_t vopretRegister; GetOpReturnData(opretRegister, vopretRegister); rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, - EncodeTokenOpRetV1(tid, voutPubkeysEmpty /*=never spent*/, { vopretRegister })); + EncodeTokenOpRet(tid, voutPubkeysEmpty /*=never spent*/, std::make_pair(OPRETID_ROGUEGAMEDATA, vopretRegister))); } } } @@ -1668,7 +1668,7 @@ UniValue games_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) gamespk = GetUnspendable(cp,0); mypk = pubkey2pk(Mypubkey()); GetCCaddress1of2(cp,coinaddr,gamespk,mypk); - SetCCtxids(txids,coinaddr,true,cp->evalcode,0,zeroid,'R'); + SetCCtxids(txids,coinaddr,true,cp->evalcode,zeroid,'R'); games_univalue(result,"games",-1,-1); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { diff --git a/src/cc/gateways.cpp b/src/cc/gateways.cpp index 17d7a0baa7a..515c0b3cb2e 100644 --- a/src/cc/gateways.cpp +++ b/src/cc/gateways.cpp @@ -14,7 +14,6 @@ ******************************************************************************/ #include "CCGateways.h" -#include "CCtokens.h" #include "key_io.h" /* @@ -55,7 +54,7 @@ In order to create a new gateway it is necessary to follow some strict steps. 1. create a token with the max possible supply that will be issued 2. transfer 100% of them to the gateways CC's global pubkey's asset CC address. (yes it is a bit confusing) - 3. create an oracle with the identical name, ie. KMD and format must start with IhhL (height, blockhash, merkleroot, deposit balance) + 3. create an oracle with the identical name, ie. KMD and format must start with Ihh (height, blockhash, merkleroot) 4. register a publisher and fund it with a subscribe. there will be a special client app that will automatically publish the merkleroots. 5. Now a gatewaysbind can bind an external coin to an asset, along with the oracle for the merkleroots. the txid from the bind is used in most of the other gateways CC calls @@ -153,8 +152,7 @@ #define KMD_P2SHTYPE 85 #define KMD_WIFTYPE 188 #define KMD_TADDR 0 -#define CC_MARKER_VALUE 1000 -#define CC_TXFEE 10000 +#define CC_MARKER_VALUE 10000 CScript EncodeGatewaysBindOpRet(uint8_t funcid,uint256 tokenid,std::string coin,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector gatewaypubkeys,uint8_t taddr,uint8_t prefix,uint8_t prefix2,uint8_t wiftype) { @@ -166,28 +164,29 @@ CScript EncodeGatewaysBindOpRet(uint8_t funcid,uint256 tokenid,std::string coin, gatewayspk = GetUnspendable(cp,0); pubkeys.push_back(gatewayspk); vopret = E_MARSHAL(ss << evalcode << funcid << coin << totalsupply << oracletxid << M << N << gatewaypubkeys << taddr << prefix << prefix2 << wiftype); - return(EncodeTokenOpRetV1(tokenid,pubkeys, { vopret })); + return(EncodeTokenOpRet(tokenid,pubkeys, std::make_pair(OPRETID_GATEWAYSDATA, vopret))); } uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,uint256 &tokenid,std::string &coin,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &gatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype) { - std::vector oprets; - std::vector vopret,vOpretExtra; uint8_t *script,e,f; std::vector pubkeys; + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; - if (DecodeTokenOpRetV1(scriptPubKey,tokenid,pubkeys,oprets)!=0 && GetOpReturnCCBlob(oprets, vOpretExtra) && vOpretExtra.size()>0) + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys,oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) { vopret=vOpretExtra; } else GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); depositaddr[0] = 0; - if ( vopret.size() > 2 && script[0]==EVAL_GATEWAYS && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> totalsupply; ss >> oracletxid; ss >> M; ss >> N; ss >> gatewaypubkeys; ss >> taddr; ss >> prefix; ss >> prefix2; ss >> wiftype) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> totalsupply; ss >> oracletxid; ss >> M; ss >> N; ss >> gatewaypubkeys; ss >> taddr; ss >> prefix; ss >> prefix2; ss >> wiftype) != 0 ) { if ( prefix == KMD_PUBTYPE && prefix2 == KMD_P2SHTYPE ) { if ( N > 1 ) { strcpy(depositaddr,CBitcoinAddress(CScriptID(GetScriptForMultisig(M,gatewaypubkeys))).ToString().c_str()); + LOGSTREAM("gatewayscc", CCLOG_DEBUG1, stream << "f." << f << " M." << (int)M << " of N." << (int)N << " size." << (int32_t)gatewaypubkeys.size() << " -> " << depositaddr << std::endl); } else Getscriptaddress(depositaddr,CScript() << ParseHex(HexStr(gatewaypubkeys[0])) << OP_CHECKSIG); } else @@ -200,99 +199,142 @@ uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,ui return(0); } -CScript EncodeGatewaysDepositOpRet(uint8_t funcid,uint256 tokenid,uint256 bindtxid,std::string refcoin,std::vector publishers,std::vectortxids,int32_t height,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount) +CScript EncodeGatewaysDepositOpRet(uint8_t funcid,uint256 bindtxid,std::string refcoin,std::vector publishers,std::vectortxids,int32_t height,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount) { - vscript_t vopret; uint8_t evalcode = EVAL_GATEWAYS; std::vector pubkeys; + CScript opret; uint8_t evalcode = EVAL_GATEWAYS; + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << refcoin << bindtxid << publishers << txids << height << cointxid << claimvout << deposithex << proof << destpub << amount); + return(opret); +} + +uint8_t DecodeGatewaysDepositOpRet(const CScript &scriptPubKey,uint256 &bindtxid,std::string &refcoin,std::vector&publishers,std::vector&txids,int32_t &height,uint256 &cointxid, int32_t &claimvout,std::string &deposithex,std::vector &proof,CPubKey &destpub,int64_t &amount) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> refcoin; ss >> bindtxid; ss >> publishers; ss >> txids; ss >> height; ss >> cointxid; ss >> claimvout; ss >> deposithex; ss >> proof; ss >> destpub; ss >> amount) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodeGatewaysClaimOpRet(uint8_t funcid,uint256 tokenid,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount) +{ + CScript opret; uint8_t evalcode = EVAL_GATEWAYS; struct CCcontract_info *cp,C; CPubKey gatewayspk; + std::vector pubkeys; + vscript_t vopret; pubkeys.push_back(destpub); - vopret = E_MARSHAL(ss << evalcode << funcid << tokenid << bindtxid << refcoin << publishers << txids << height << cointxid << claimvout << deposithex << proof << destpub << amount); - return(EncodeTokenOpRetV1(tokenid,pubkeys, { vopret })); + vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << funcid << bindtxid << refcoin << deposittxid << destpub << amount); + return(EncodeTokenOpRet(tokenid,pubkeys, make_pair(OPRETID_GATEWAYSDATA, vopret))); } -uint8_t DecodeGatewaysDepositOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &bindtxid,std::string &refcoin,std::vector&publishers,std::vector&txids,int32_t &height,uint256 &cointxid, int32_t &claimvout,std::string &deposithex,std::vector &proof,CPubKey &destpub,int64_t &amount) +uint8_t DecodeGatewaysClaimOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &bindtxid,std::string &refcoin,uint256 &deposittxid,CPubKey &destpub,int64_t &amount) { - std::vector vopret,vOpretExtra; uint8_t *script,e,f; std::vector pubkeys; - std::vector oprets; + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; - if (DecodeTokenOpRetV1(scriptPubKey,tokenid,pubkeys, oprets)!=0 && GetOpReturnCCBlob(oprets, vOpretExtra) && vOpretExtra.size()>0) + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) { vopret=vOpretExtra; } else GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0]==EVAL_GATEWAYS && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> tokenid; ss >> bindtxid; ss >> refcoin; ss >> publishers; ss >> txids; ss >> height; ss >> cointxid; ss >> claimvout; ss >> deposithex; ss >> proof; ss >> destpub; ss >> amount) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bindtxid; ss >> refcoin; ss >> deposittxid; ss >> destpub; ss >> amount) != 0 ) { return(f); } return(0); } -CScript EncodeGatewaysWithdrawOpRet(uint8_t funcid,uint256 tokenid,uint256 bindtxid,CPubKey mypk,std::string refcoin,CPubKey withdrawpub,int64_t amount) +CScript EncodeGatewaysWithdrawOpRet(uint8_t funcid,uint256 tokenid,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) { - uint8_t evalcode = EVAL_GATEWAYS; struct CCcontract_info *cp,C; CPubKey gatewayspk; + CScript opret; uint8_t evalcode = EVAL_GATEWAYS; struct CCcontract_info *cp,C; CPubKey gatewayspk; std::vector pubkeys; vscript_t vopret; cp = CCinit(&C,EVAL_GATEWAYS); gatewayspk = GetUnspendable(cp,0); pubkeys.push_back(gatewayspk); - vopret = E_MARSHAL(ss << evalcode << funcid << bindtxid << mypk << refcoin << withdrawpub << amount); - return(EncodeTokenOpRetV1(tokenid,pubkeys, { vopret })); + vopret = /*opret << OP_RETURN << */ E_MARSHAL(ss << evalcode << funcid << bindtxid << refcoin << withdrawpub << amount); + return(EncodeTokenOpRet(tokenid,pubkeys, std::make_pair(OPRETID_GATEWAYSDATA, vopret))); } -uint8_t DecodeGatewaysWithdrawOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &bindtxid,CPubKey &mypk,std::string &refcoin,CPubKey &withdrawpub,int64_t &amount) +uint8_t DecodeGatewaysWithdrawOpRet(const CScript &scriptPubKey, uint256& tokenid, uint256 &bindtxid, std::string &refcoin, CPubKey &withdrawpub, int64_t &amount) { - std::vector oprets; - std::vector vopret,vOpretExtra; uint8_t *script,e,f; std::vector pubkeys; + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; - if (DecodeTokenOpRetV1(scriptPubKey,tokenid,pubkeys, oprets)!=0 && GetOpReturnCCBlob(oprets, vOpretExtra) && vOpretExtra.size()>0) + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) { vopret=vOpretExtra; } else GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0]==EVAL_GATEWAYS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bindtxid; ss >> mypk; ss >> refcoin; ss >> withdrawpub; ss >> amount) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bindtxid; ss >> refcoin; ss >> withdrawpub; ss >> amount) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodeGatewaysPartialOpRet(uint8_t funcid, uint256 withdrawtxid,std::string refcoin,uint8_t K, CPubKey signerpk,std::string hex) +{ + CScript opret; uint8_t evalcode = EVAL_GATEWAYS; + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << signerpk << hex); + return(opret); +} + +uint8_t DecodeGatewaysPartialOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,CPubKey &signerpk,std::string &hex) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> K; ss >> signerpk; ss >> hex) != 0 ) { return(f); } return(0); } -CScript EncodeGatewaysWithdrawSignOpRet(uint8_t funcid,uint256 withdrawtxid, uint256 lasttxid,std::vector signingpubkeys,std::string refcoin,uint8_t K,std::string hex) +CScript EncodeGatewaysCompleteSigningOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint8_t K,std::string hex) { CScript opret; uint8_t evalcode = EVAL_GATEWAYS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << lasttxid << signingpubkeys << refcoin << K << hex); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << hex); return(opret); } -uint8_t DecodeGatewaysWithdrawSignOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid, uint256 &lasttxid,std::vector &signingpubkeys,std::string &refcoin,uint8_t &K,std::string &hex) +uint8_t DecodeGatewaysCompleteSigningOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,std::string &hex) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0]==EVAL_GATEWAYS && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> lasttxid; ss >> signingpubkeys; ss >> refcoin; ss >> K; ss >> hex) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> K; ss >> hex) != 0 ) { return(f); } return(0); } -CScript EncodeGatewaysMarkDoneOpRet(uint8_t funcid,uint256 withdrawtxid,CPubKey mypk,std::string refcoin,uint256 withdrawsigntxid) +CScript EncodeGatewaysMarkDoneOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint256 completetxid) { CScript opret; uint8_t evalcode = EVAL_GATEWAYS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << mypk << refcoin << withdrawsigntxid); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << completetxid); return(opret); } -uint8_t DecodeGatewaysMarkDoneOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,CPubKey &mypk,std::string &refcoin, uint256 &withdrawsigntxid) +uint8_t DecodeGatewaysMarkDoneOpRet(const CScript &scriptPubKey, uint256 &withdrawtxid, std::string &refcoin, uint256 &completetxid) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0]==EVAL_GATEWAYS && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> mypk; ss >> refcoin; ss >> withdrawsigntxid;) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> completetxid;) != 0 ) { return(f); } @@ -301,10 +343,10 @@ uint8_t DecodeGatewaysMarkDoneOpRet(const CScript &scriptPubKey,uint256 &withdra uint8_t DecodeGatewaysOpRet(const CScript &scriptPubKey) { - std::vector oprets; - std::vector vopret,vOpretExtra; uint8_t *script,e,f; std::vector pubkeys; uint256 tokenid; + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; uint256 tokenid; - if (DecodeTokenOpRetV1(scriptPubKey,tokenid,pubkeys, oprets)!=0 && GetOpReturnCCBlob(oprets, vOpretExtra) && vOpretExtra.size()>0) + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) { vopret=vOpretExtra; } @@ -313,7 +355,7 @@ uint8_t DecodeGatewaysOpRet(const CScript &scriptPubKey) if ( vopret.size() > 2 && script[0] == EVAL_GATEWAYS) { f=script[1]; - if (f == 'B' || f == 'D' || f == 'C' || f == 'W' || f == 'S' || f == 'M') + if (f == 'B' || f == 'D' || f == 'C' || f == 'W' || f == 'P' || f == 'S' || f == 'M') return(f); } return(0); @@ -364,7 +406,7 @@ bool GatewaysExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti } if ( inputs != outputs+txfee ) { - LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "inputs " << (long long)inputs << " vs outputs " << (long long)outputs << std::endl); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "inputs " << (long long)inputs << " vs outputs " << (long long)outputs << std::endl); return eval->Invalid("mismatched inputs != outputs + txfee"); } else return(true); @@ -377,23 +419,23 @@ int64_t GatewaysVerify(char *refdepositaddr,uint256 oracletxid,int32_t claimvout if ( myGetTransaction(oracletxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) { - LOGSTREAM("gatewayscc",CCLOG_ERROR, stream << "GatewaysVerify cant find oracletxid " << oracletxid.GetHex() << std::endl); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify cant find oracletxid " << oracletxid.GetHex() << std::endl); return(0); } if ( DecodeOraclesCreateOpRet(tx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' || name != refcoin ) { - LOGSTREAM("gatewayscc",CCLOG_ERROR, stream << "GatewaysVerify mismatched oracle name " << name << " != " << refcoin << std::endl); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify mismatched oracle name " << name << " != " << refcoin << std::endl); return(0); } proofroot = BitcoinGetProofMerkleRoot(proof,txids); if ( proofroot != merkleroot ) { - LOGSTREAM("gatewayscc",CCLOG_ERROR, stream << "GatewaysVerify mismatched merkleroot " << proofroot.GetHex() << " != " << merkleroot.GetHex() << std::endl); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify mismatched merkleroot " << proofroot.GetHex() << " != " << merkleroot.GetHex() << std::endl); return(0); } if (std::find(txids.begin(), txids.end(), cointxid) == txids.end()) { - LOGSTREAM("gatewayscc",CCLOG_ERROR, stream << "GatewaysVerify invalid proof for this cointxid" << std::endl); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify invalid proof for this cointxid" << std::endl); return 0; } if ( DecodeHexTx(tx,deposithex) != 0 ) @@ -416,18 +458,18 @@ int64_t GatewaysVerify(char *refdepositaddr,uint256 oracletxid,int32_t claimvout { LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "verified proof for cointxid in merkleroot" << std::endl); return(nValue); - } else LOGSTREAM("gatewayscc",CCLOG_ERROR, stream << "(" << refdepositaddr << ") != (" << destaddr << ") or txid " << txid.GetHex() << " mismatch." << (txid!=cointxid) << " or script mismatch" << std::endl); - } else LOGSTREAM("gatewayscc",CCLOG_ERROR, stream << "claimaddr." << claimaddr << " != destpubaddr." << destpubaddr << std::endl); + } else LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "(" << refdepositaddr << ") != (" << destaddr << ") or txid " << txid.GetHex() << " mismatch." << (txid!=cointxid) << " or script mismatch" << std::endl); + } else LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "claimaddr." << claimaddr << " != destpubaddr." << destpubaddr << std::endl); } return(0); } int64_t GatewaysDepositval(CTransaction tx,CPubKey mypk) { - int32_t numvouts,claimvout,height; int64_t amount; std::string coin,deposithex; std::vector publishers; std::vectortxids; uint256 tokenid,bindtxid,cointxid; std::vector proof; CPubKey claimpubkey; + int32_t numvouts,claimvout,height; int64_t amount; std::string coin,deposithex; std::vector publishers; std::vectortxids; uint256 bindtxid,cointxid; std::vector proof; CPubKey claimpubkey; if ( (numvouts= tx.vout.size()) > 0 ) { - if ( DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,bindtxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,claimpubkey,amount) == 'D' && claimpubkey == mypk ) + if ( DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,claimpubkey,amount) == 'D' && claimpubkey == mypk ) { return(amount); } @@ -442,7 +484,7 @@ int32_t GatewaysBindExists(struct CCcontract_info *cp,CPubKey gatewayspk,uint256 std::vector txids; _GetCCaddress(markeraddr,EVAL_GATEWAYS,gatewayspk); - SetCCtxids(txids,markeraddr,true,EVAL_GATEWAYS,CC_MARKER_VALUE,zeroid,'B'); + SetCCtxids(txids,markeraddr,true,EVAL_GATEWAYS,zeroid,'B'); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { if ( myGetTransaction(*it,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey)=='B' ) @@ -451,7 +493,7 @@ int32_t GatewaysBindExists(struct CCcontract_info *cp,CPubKey gatewayspk,uint256 { if ( tokenid == reftokenid ) { - LOGSTREAM("gatewayscc",CCLOG_ERROR, stream << "trying to bind an existing tokenid" << std::endl); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "trying to bind an existing tokenid" << std::endl); return(1); } } @@ -463,7 +505,7 @@ int32_t GatewaysBindExists(struct CCcontract_info *cp,CPubKey gatewayspk,uint256 { const CTransaction &txmempool = *it; - if ((numvouts=txmempool.vout.size()) > 0 && txmempool.vout[0].nValue==CC_MARKER_VALUE && DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey)=='B') + if ((numvouts=txmempool.vout.size()) > 0 && DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey)=='B') if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B' && tokenid == reftokenid) return(1); @@ -472,19 +514,13 @@ int32_t GatewaysBindExists(struct CCcontract_info *cp,CPubKey gatewayspk,uint256 return(0); } -bool CheckSupply(const CTransaction& tx,char *gatewaystokensaddr,int64_t totalsupply) -{ - for (int i=0;i<100;i++) if (ConstrainVout(tx.vout[0],1,gatewaystokensaddr,totalsupply/100)==0) return (false); - return (true); -} - bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn) { - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,height,claimvout; bool retval; uint8_t funcid,K,tmpK,M,N,taddr,prefix,prefix2,wiftype; + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks,height,claimvout; bool retval; uint8_t funcid,hash[32],K,M,N,taddr,prefix,prefix2,wiftype; char str[65],destaddr[65],depositaddr[65],gatewaystokensaddr[65],validationError[512]; - std::vector txids; std::vector pubkeys,publishers,signingpubkeys,tmpsigningpubkeys; std::vector proof; int64_t fullsupply,totalsupply,amount,tmpamount; - uint256 hashblock,txid,bindtxid,deposittxid,withdrawtxid,tmpwithdrawtxid,withdrawsigntxid,lasttxid,tmplasttxid,tokenid,tmptokenid,oracletxid,cointxid,tmptxid,merkleroot,mhash; - CTransaction tmptx; std::string refcoin,tmprefcoin,hex,tmphex,name,description,format; CPubKey pubkey,tmppubkey,gatewayspk,destpub; + std::vector txids; std::vector pubkeys,publishers,tmppublishers; std::vector proof; int64_t fullsupply,totalsupply,amount,tmpamount; + uint256 hashblock,txid,bindtxid,deposittxid,withdrawtxid,completetxid,tokenid,tmptokenid,oracletxid,bindtokenid,cointxid,tmptxid,merkleroot,mhash; CTransaction bindtx,tmptx; + std::string refcoin,tmprefcoin,hex,name,description,format; CPubKey pubkey,tmppubkey,gatewayspk; numvins = tx.vin.size(); numvouts = tx.vout.size(); @@ -494,14 +530,12 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & else { //LogPrint("gatewayscc-1","check amounts\n"); - // if ( GatewaysExactAmounts(cp,eval,tx,1,CC_TXFEE) == false ) + // if ( GatewaysExactAmounts(cp,eval,tx,1,10000) == false ) // { // return eval->Invalid("invalid inputs vs. outputs!"); // } // else // { - CCOpretCheck(eval,tx,true,true,true); - ExactAmounts(eval,tx,CC_TXFEE); gatewayspk = GetUnspendable(cp,0); GetTokensCCaddress(cp, gatewaystokensaddr, gatewayspk); if ( (funcid = DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey)) != 0) @@ -517,40 +551,35 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & return eval->Invalid("unexpected GatewaysValidate for gatewaysbind!"); break; case 'D': + //vin.0: normal input + //vout.0: CC vout marker to destination pubkey + //vout.1: normal output marker to txidaddr + //vout.n-1: opreturn - 'D' bindtxid coin publishers txids height cointxid claimvout deposithex proof destpub amount + return eval->Invalid("unexpected GatewaysValidate for gatewaysdeposit!"); + break; + case 'C': //vin.0: normal input //vin.1: CC input of gateways tokens + //vin.2: CC input of marker from gatewaysdeposit tx //vout.0: CC vout of tokens from deposit amount to destinatoin pubkey - //vout.1: normal output marker to txidaddr - //vout.2: CC vout change of gateways tokens to gateways tokens CC address (if any) + //vout.1: CC vout change of gateways tokens to gateways tokens CC address (if any) //vout.n-1: opreturn - 'C' tokenid bindtxid coin deposittxid destpub amount - if ((numvouts=tx.vout.size()) < 1 || DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,publishers,txids,height,cointxid,claimvout,hex,proof,pubkey,amount) != 'D') - return eval->Invalid("invalid gatewaysdeposit OP_RETURN data!"); - else if ( CCCointxidExists("gatewayscc-1",tx.GetHash(),cointxid) != 0 ) - return eval->Invalid("cointxid " + cointxid.GetHex() + " already processed with gatewaysdeposit!"); - else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for gatewaysdeposit!"); - else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for gatewaysdeposit!"); - else if (_GetCCaddress(destaddr,EVAL_TOKENS,pubkey)==0 || ConstrainVout(tx.vout[0],1,destaddr,amount)==0) - return eval->Invalid("invalid vout tokens to destpub for gatewaysdeposit!"); - else if ( CCtxidaddr(destaddr,cointxid)==CPubKey() || ConstrainVout(tx.vout[1],0,destaddr,CC_MARKER_VALUE)==0) - return eval->Invalid("invalid marker vout for gatewaysdeposit!"); - else if (numvouts>2 && (GetTokensCCaddress(cp,destaddr,gatewayspk)==0 || ConstrainVout(tx.vout[2],1,destaddr,0)==0)) - return eval->Invalid("invalid vout tokens change to gateways global address for gatewaysdeposit!"); + if ((numvouts=tx.vout.size()) < 1 || DecodeGatewaysClaimOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,refcoin,deposittxid,pubkey,amount)!='C') + return eval->Invalid("invalid gatewaysclaim OP_RETURN data!"); else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) return eval->Invalid("invalid gatewaysbind txid!"); - else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,refcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) return eval->Invalid("vin.0 is normal for gatewaysbind!"); - else if ( CheckSupply(tmptx,gatewaystokensaddr,totalsupply)==0) - return eval->Invalid("invalid tokens to gateways vouts for gatewaysbind!"); - else if ( tmptx.vout.size()<102 || ConstrainVout(tmptx.vout[100],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + else if ( ConstrainVout(tmptx.vout[0],1,gatewaystokensaddr,totalsupply)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysbind!"); + else if ( ConstrainVout(tmptx.vout[1],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) return eval->Invalid("invalid marker vout for gatewaysbind!"); else if (tmprefcoin!=refcoin) return eval->Invalid("refcoin different than in bind tx"); else if (tmptokenid!=tokenid) - return eval->Invalid("tokenid different than in bind tx"); + return eval->Invalid("tokenid does not match tokenid from gatewaysbind"); else if ( N == 0 || N > 15 || M > N ) return eval->Invalid("invalid MofN in gatewaysbind"); else if (pubkeys.size()!=N) @@ -575,19 +604,50 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & sprintf(validationError,"mismatched oracle name %s != %s\n",name.c_str(),refcoin.c_str()); return eval->Invalid(validationError); } - else if (format.size()!=4 || strncmp(format.c_str(),"IhhL",4)!=0) + else if (format.size()!=3 || strncmp(format.c_str(),"Ihh",3)!=0) { - sprintf(validationError,"illegal format %s != IhhL\n",format.c_str()); + sprintf(validationError,"illegal format %s != Ihh\n",format.c_str()); return eval->Invalid(validationError); } else if (komodo_txnotarizedconfirmed(bindtxid) == false) return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(deposittxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysdeposittxid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysDepositOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptxid,tmprefcoin,tmppublishers,txids,height,cointxid,claimvout,hex,proof,tmppubkey,tmpamount) != 'D') + return eval->Invalid("invalid gatewaysdeposit OP_RETURN data!"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysdeposit!"); + else if ( GetCCaddress(cp,destaddr,tmppubkey)==0 || ConstrainVout(tmptx.vout[0],1,destaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid CC marker vout for gatewaysdeposit!"); + else if ( CCtxidaddr(destaddr,cointxid)==CPubKey() || ConstrainVout(tmptx.vout[1],0,destaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid normal marker vout for gatewaysdeposit!"); else if (tmprefcoin!=refcoin) - return eval->Invalid("refcoin different than in bind tx"); - else if (!pubkey.IsFullyValid()) - return eval->Invalid("invalid deposit tx destination pubkey"); - else if (amount>totalsupply) + return eval->Invalid("refcoin different than in deposit tx"); + else if (bindtxid!=tmptxid) + return eval->Invalid("bindtxid does not match to bindtxid from gatewaysdeposit"); + else if (tmpamount>totalsupply) return eval->Invalid("deposit amount greater then bind total supply"); + else if (komodo_txnotarizedconfirmed(deposittxid) == false) + return eval->Invalid("gatewaysdeposit tx is not yet confirmed(notarised)!"); + else if (tx.vin.size()>0) + { + i=0; + while (iInvalid("vin."+std::to_string(i)+" is CC for gatewaysclaim!"); + i++; + } + } + else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewaysclaim or invalid marker amount!"); + else if (_GetCCaddress(destaddr,EVAL_TOKENS,pubkey)==0 || ConstrainVout(tx.vout[0],1,destaddr,amount)==0) + return eval->Invalid("invalid vout tokens to destpub for gatewaysclaim!"); + else if (numvouts>2 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() && (myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || ConstrainVout(tx.vout[1],1,gatewaystokensaddr,tmptx.vout[tx.vin[1].prevout.n].nValue-amount)==0)) + return eval->Invalid("invalid CC change vout for gatewaysclaim!"); + else if (amount!=tmpamount) + return eval->Invalid("claimed amount different then deposit amount"); + else if (pubkey!=tmppubkey) + return eval->Invalid("claim destination pubkey different than in deposit tx"); else { int32_t m; @@ -622,31 +682,67 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & //vout.n-1: opreturn - 'W' tokenid bindtxid refcoin withdrawpub amount return eval->Invalid("unexpected GatewaysValidate for gatewaysWithdraw!"); break; + case 'P': + //vin.0: normal input + //vin.1: CC input of marker from previous tx (withdraw or partialsing) + //vout.0: CC vout marker to gateways CC address + //vout.n-1: opreturn - 'P' withdrawtxid refcoin number_of_signs mypk hex + if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,pubkey,hex)!='P') + return eval->Invalid("invalid gatewaysPartialSign OP_RETURN data!"); + else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); + else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (tmptokenid!=tokenid) + return eval->Invalid("tokenid does not match tokenid from gatewaysbind"); + else if (komodo_txnotarizedconfirmed(bindtxid) == false) + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewayspartialsign!"); + else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewayspartialsign or invalid marker amount!"); + else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) + return eval->Invalid("vout.0 invalid marker for gatewayspartialsign!"); + else if (K>M) + return eval->Invalid("invalid number of signs!"); + break; case 'S': //vin.0: normal input - //vin.1: CC input of marker from previous tx (withdraw or withdrawsign) - //vout.0: CC vout marker to gateway CC address - //vout.n-1: opreturn - 'S' withdrawtxid lasttxid mypk refcoin number_of_signs hex - if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,lasttxid,signingpubkeys,refcoin,K,hex)!='S') - return eval->Invalid("invalid gatewayswithdrawsign OP_RETURN data!"); - else if (lasttxid!=withdrawtxid && myGetTransaction(lasttxid,tmptx,hashblock) == 0) - return eval->Invalid("invalid last txid!"); - else if (lasttxid!=withdrawtxid && (numvouts=tx.vout.size()) > 0 && DecodeGatewaysWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,tmpwithdrawtxid,tmplasttxid,tmpsigningpubkeys,tmprefcoin,tmpK,tmphex)!='S') - return eval->Invalid("invalid last gatewayswithdrawsign OP_RETURN data!"); - else if (lasttxid!=withdrawtxid && CompareHexVouts(hex,tmphex)==0) - return eval->Invalid("invalid gatewayswithdrawsign, modifying initial tx vouts in hex!"); + //vin.1: CC input of marker from previous tx (withdraw or partialsing) + //vout.0: CC vout marker to gateways CC address + //vout.n-1: opreturn - 'S' withdrawtxid refcoin hex + if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,hex)!='S') + return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!"); else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) - return eval->Invalid("invalid gatewayswithdraw txid!"); - else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmppubkey,tmprefcoin,destpub,amount)!='W') + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,pubkey,amount)!='W') return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); else if (tmprefcoin!=refcoin) - return eval->Invalid("refcoin different than in withdraw tx"); + return eval->Invalid("refcoin different than in bind tx"); else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for gatewayswithdraw!"); + return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) - return eval->Invalid("invalid marker vout for gatewayswithdraw!"); + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0) - return eval->Invalid("invalid destination of tokens or amount in gatewayswithdraw!"); + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); + else if (tmptx.vout[1].nValue!=amount) + return eval->Invalid("amount in opret not matching tx tokens amount!"); else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) @@ -656,47 +752,36 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & else if (tmprefcoin!=refcoin) return eval->Invalid("refcoin different than in bind tx"); else if (tmptokenid!=tokenid) - return eval->Invalid("tokenid different than in bind tx"); - else if ( N == 0 || N > 15 || M > N ) - return eval->Invalid("invalid N or M for gatewaysbind"); - else if (pubkeys.size()!=N) - return eval->Invalid("not enough pubkeys supplied in gatewaysbind for given N"); + return eval->Invalid("tokenid does not match tokenid from gatewaysbind"); else if (komodo_txnotarizedconfirmed(bindtxid) == false) return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); else if (IsCCInput(tx.vin[0].scriptSig) != 0) - return eval->Invalid("vin.0 is normal for gatewayswithdrawsign!"); - else if (CheckVinPk(tx,0,pubkeys)==0) - return eval->Invalid("vin.0 invalid, gatewayswithdrawsign must be created by one of the gateways pubkeys owner!"); - else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) - return eval->Invalid("vin.1 is CC marker for gatewayswithdrawsign or invalid marker amount!"); + return eval->Invalid("vin.0 is normal for gatewayscompletesigning!"); + else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewayscompletesigning or invalid marker amount!"); else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) - return eval->Invalid("vout.0 invalid marker for gatewayswithdrawsign!"); - else if (std::find(pubkeys.begin(),pubkeys.end(),signingpubkeys[signingpubkeys.size()-1])==pubkeys.end()) - return eval->Invalid("invalid pubkey in gatewayswithdrawsign OP_RETURN, must be one of gateways pubkeys!"); + return eval->Invalid("vout.0 invalid marker for gatewayscompletesigning!"); + else if (KInvalid("invalid number of signs!"); break; case 'M': //vin.0: normal input - //vin.1: CC input of gatewaywithdrawsign tx marker to gateway CC address - //vout.0; marker value back to users pubkey - //vout.n-1: opreturn - 'M' withdrawtxid mypk refcoin withdrawsigntxid - if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysMarkDoneOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,pubkey,refcoin,withdrawsigntxid)!='M') + //vin.1: CC input of gatewayscompletesigning tx marker to gateways CC address + //vout.0: opreturn - 'M' withdrawtxid refcoin completetxid + if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysMarkDoneOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,completetxid)!='M') return eval->Invalid("invalid gatewaysmarkdone OP_RETURN data!"); - else if (myGetTransaction(withdrawsigntxid,tmptx,hashblock) == 0) - return eval->Invalid("invalid gatewayswithdrawsign txid!"); - else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawSignOpRet(tmptx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,tmprefcoin,K,hex)!='S') - return eval->Invalid("invalid gatewayswithdrawsign OP_RETURN data!"); + else if (myGetTransaction(completetxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewayscompletesigning txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysCompleteSigningOpRet(tmptx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmprefcoin,K,hex)!='S') + return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!"); + else if (komodo_txnotarizedconfirmed(completetxid) == false) + return eval->Invalid("gatewayscompletesigning tx is not yet confirmed(notarised)!"); else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) - return eval->Invalid("invalid gatewayswithdraw txid!"); - else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmppubkey,tmprefcoin,destpub,amount)!='W') + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,pubkey,amount)!='W') return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); else if (tmprefcoin!=refcoin) - return eval->Invalid("refcoin different than in withdraw tx"); - else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for gatewayswithdraw!"); - else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) - return eval->Invalid("invalid marker vout for gatewayswithdraw!"); - else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0) - return eval->Invalid("invalid destination of tokens or amount in gatewayswithdraw!"); + return eval->Invalid("refcoin different than in bind tx"); else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) @@ -706,27 +791,21 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & else if (tmprefcoin!=refcoin) return eval->Invalid("refcoin different than in bind tx"); else if (tmptokenid!=tokenid) - return eval->Invalid("tokenid different than in bind tx"); - else if ( N == 0 || N > 15 || M > N ) - return eval->Invalid("invalid N or M for gatewaysbind"); - else if (pubkeys.size()!=N) - return eval->Invalid("not enough pubkeys supplied in gatewaysbind for given N"); + return eval->Invalid("tokenid does not match tokenid from gatewaysbind"); else if (komodo_txnotarizedconfirmed(bindtxid) == false) return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) return eval->Invalid("vin.0 is normal for gatewaysmarkdone!"); - else if (CheckVinPk(tx,0,pubkeys)==0) - return eval->Invalid("vin.0 invalid, gatewaysmarkdone must be created by one of the gateways pubkeys owner!"); - else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) - return eval->Invalid("vin.1 is CC marker for gatewaysmarkdone or invalid marker amount!"); - else if (std::find(pubkeys.begin(),pubkeys.end(),signingpubkeys[signingpubkeys.size()-1])==pubkeys.end()) - return eval->Invalid("invalid pubkey in gatewaysmarkdone OP_RETURN, must be one of gateways pubkeys!"); - break; + else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewaysmarkdone or invalid marker amount!"); + else if (KInvalid("invalid number of signs!"); + break; } } retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); if ( retval != 0 ) - LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "Gateways tx validated" << std::endl); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "Gateways tx validated" << std::endl); else fprintf(stderr,"Gateways tx invalid\n"); return(retval); // } @@ -739,9 +818,9 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 bindtxid,int64_t total,int32_t maxinputs) { char coinaddr[64],depositaddr[64]; int64_t threshold,nValue,price,totalinputs = 0,totalsupply,amount; - CTransaction vintx,bindtx; int32_t vout,numvouts,n = 0,height,claimvout; uint8_t M,N,evalcode,funcid,taddr,prefix,prefix2,wiftype; std::vector pubkeys,publishers; - std::vector > unspentOutputs; std::string refcoin,tmprefcoin,hex; CPubKey withdrawpub,destpub,tmppk,pubkey; - uint256 tokenid,txid,oracletxid,tmpbindtxid,tmptokenid,deposittxid,hashBlock,cointxid; std::vector txids; std::vector proof; + CTransaction vintx,bindtx; int32_t vout,numvouts,n = 0; uint8_t M,N,evalcode,funcid,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + std::vector > unspentOutputs; std::string refcoin,tmprefcoin; CPubKey withdrawpub,destpub; + uint256 tokenid,txid,oracletxid,tmpbindtxid,tmptokenid,deposittxid,hashBlock; if ( myGetTransaction(bindtxid,bindtx,hashBlock) != 0 ) { @@ -754,7 +833,7 @@ int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP if ( maxinputs > 0 ) threshold = total/maxinputs; else threshold = total; - LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "check " << coinaddr << " for gateway inputs" << std::endl); + LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "check " << coinaddr << " for gateway inputs" << std::endl); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -762,33 +841,33 @@ int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP if ( myGetTransaction(txid,vintx,hashBlock) != 0 ) { funcid=DecodeGatewaysOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey); - if (((funcid=='B' && bindtxid==txid) || - (vout==2 && funcid=='D' && DecodeGatewaysDepositOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmprefcoin,publishers,txids,height,cointxid,claimvout,hex,proof,pubkey,amount) == 'D' && - tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid) || - (vout==2 && funcid=='W' && DecodeGatewaysWithdrawOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmppk,tmprefcoin,withdrawpub,amount) == 'W' && - tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid)) && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout)==0 && total != 0 && maxinputs != 0) + if ((vout==0 && funcid=='B' && bindtxid==txid && total != 0 && maxinputs != 0) || + (vout==1 && funcid=='W' && DecodeGatewaysWithdrawOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmprefcoin,withdrawpub,amount) == 'W' && + tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid && total != 0 && maxinputs != 0) || + (vout==1 && funcid=='C' && DecodeGatewaysClaimOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmprefcoin,deposittxid,destpub,amount) == 'C' && + tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid && total != 0 && maxinputs != 0)) { mtx.vin.push_back(CTxIn(txid,vout,CScript())); totalinputs += it->second.satoshis; n++; - } - if ( totalinputs >= total || (maxinputs > 0 && n >= maxinputs)) break; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break; + } } } return(totalinputs); } - else LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "invalid GatewaysBind" << std::endl); + else LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "invalid GatewaysBind" << std::endl); } - else LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "can't find GatewaysBind txid" << std::endl); + else LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "can't find GatewaysBind txid" << std::endl); return(0); } UniValue GatewaysBind(const CPubKey& pk, uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction oracletx,tx; uint8_t taddr,prefix,prefix2,wiftype; CPubKey mypk,gatewayspk,regpk; CScript opret; uint256 hashBlock,txid,tmporacletxid; - struct CCcontract_info *cp,*cpTokens,C,CTokens; std::string name,description,format; int32_t i,numvouts,n=0; int64_t fullsupply,datafee; - char destaddr[64],coinaddr[64],myTokenCCaddr[64],markeraddr[64],*fstr; std::vector > unspentOutputs; + CTransaction oracletx; uint8_t taddr,prefix,prefix2,wiftype; CPubKey mypk,gatewayspk; CScript opret; uint256 hashBlock; + struct CCcontract_info *cp,*cpTokens,C,CTokens; std::string name,description,format; int32_t i,numvouts; int64_t fullsupply; + char destaddr[64],coinaddr[64],myTokenCCaddr[64],*fstr; cp = CCinit(&C,EVAL_GATEWAYS); cpTokens = CCinit(&CTokens,EVAL_TOKENS); @@ -808,93 +887,70 @@ UniValue GatewaysBind(const CPubKey& pk, uint64_t txfee,std::string coin,uint256 LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "set prefix " << prefix << ", prefix2 " << prefix2 << ", wiftype " << wiftype << ", taddr " << taddr << " for " << coin << std::endl); } if ( N == 0 || N > 15 || M > N ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "illegal M." << M << " or N." << N); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "illegal M." << M << " or N." << N); if ( pubkeys.size() != N ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "M."<< M << " N." << N << " but pubkeys[" <<( int32_t)pubkeys.size() << "]"); - CCtxidaddr(markeraddr,oracletxid); - SetCCunspents(unspentOutputs,markeraddr,false); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 - && DecodeOraclesOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid) - { - std::vector::iterator it1 = std::find(pubkeys.begin(), pubkeys.end(), regpk); - if (it1 != pubkeys.end()) - n++; - } - } - if (pubkeys.size()!=n) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "different number of bind and oracle pubkeys " << n << "!=" << pubkeys.size() << std::endl); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "M."<< M << " N." << N << " but pubkeys[" <<( int32_t)pubkeys.size() << "]"); for (i=0; iunspendableCCaddr << " totalsupply " << (double)totalsupply/COIN << " != fullsupply " << (double)fullsupply/COIN); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "Gateway bind." << coin << " ("<< tokenid.GetHex() << ") globaladdr." <unspendableCCaddr << " totalsupply " << (double)totalsupply/COIN << " != fullsupply " << (double)fullsupply/COIN); if ( CCtoken_balance(myTokenCCaddr,tokenid) != totalsupply ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "token balance on " << myTokenCCaddr << " " << (double)CCtoken_balance((char *)myTokenCCaddr,tokenid)/COIN << "!=" << (double)totalsupply/COIN); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "token balance on " << myTokenCCaddr << " " << (double)CCtoken_balance((char *)myTokenCCaddr,tokenid)/COIN << "!=" << (double)totalsupply/COIN); if ( myGetTransaction(oracletxid,oracletx,hashBlock) == 0 || (numvouts= oracletx.vout.size()) <= 0 ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cant find oracletxid " << oracletxid.GetHex()); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "invalid oraclescreate opret data"); - if ( name!=coin ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "mismatched oracle name "<= txfee+CC_MARKER_VALUE ) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "Gateway bind." << coin << " (" << tokenid.GetHex() << ") already exists"); + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,2,pk.IsValid()) > 0 ) { - if (AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, totalsupply, 64)==totalsupply) + if (AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, totalsupply, 64)>0) { - for (int i=0; i<100; i++) mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,totalsupply/100,gatewayspk)); + mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,totalsupply,gatewayspk)); mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,gatewayspk)); return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysBindOpRet('B',tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype))); } - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "you must have total supply of tokens in your tokens address!"); } - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cant find enough inputs"); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find enough inputs"); } UniValue GatewaysDeposit(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction tx; CPubKey mypk,gatewayspk; uint256 oracletxid,merkleroot,mhash,hashBlock,tokenid,txid; - int64_t totalsupply,inputs; int32_t i,m,n,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::string coin; struct CCcontract_info *cp,C; + CTransaction tx; CPubKey mypk; uint256 oracletxid,merkleroot,mhash,hashBlock,tokenid,txid; + int64_t totalsupply; int32_t i,m,n,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::string coin; struct CCcontract_info *cp,C; std::vector pubkeys,publishers; std::vectortxids; char str[65],depositaddr[64],txidaddr[64]; cp = CCinit(&C,EVAL_GATEWAYS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_GATEWAYS]?0:CC_TXFEE; - gatewayspk = GetUnspendable(cp,0); + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "GatewaysDeposit ht." << height << " " << refcoin << " " << (double)amount/COIN << " numpks." << (int32_t)pubkeys.size() << std::endl); + LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "GatewaysDeposit ht." << height << " " << refcoin << " " << (double)amount/COIN << " numpks." << (int32_t)pubkeys.size() << std::endl); if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cant find bindtxid " << bindtxid.GetHex()); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find bindtxid " << bindtxid.GetHex()); if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "invalid coin - bindtxid " << bindtxid.GetHex() << " coin." << coin); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid coin - bindtxid " << bindtxid.GetHex() << " coin." << coin); if (komodo_txnotarizedconfirmed(bindtxid)==false) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "gatewaysbind tx not yet confirmed/notarized"); - if (!destpub.IsFullyValid()) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "destination pubkey is invalid"); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "gatewaysbind tx not yet confirmed/notarized"); n = (int32_t)pubkeys.size(); merkleroot = zeroid; for (i=m=0; i= txfee+CC_MARKER_VALUE ) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "deposittxid didnt validate"); + if ( AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,3,pk.IsValid()) > 0 ) { - if ((inputs=AddGatewaysInputs(cp, mtx, gatewayspk, bindtxid, amount, 60)) >= amount) - { - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,amount,destpub)); - mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,cointxid))) << OP_CHECKSIG)); - if ( inputs > amount ) mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,inputs-amount,gatewayspk)); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysDepositOpRet('D',tokenid,bindtxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,destpub,amount))); - } - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cant find enough token inputs from gateway"); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,destpub)); + mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,cointxid))) << OP_CHECKSIG)); + return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysDepositOpRet('D',bindtxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,destpub,amount))); + } + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find enough inputs"); +} + +UniValue GatewaysClaim(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction tx; CPubKey mypk,gatewayspk,tmpdestpub; struct CCcontract_info *cp,C; uint8_t M,N,taddr,prefix,prefix2,wiftype; + std::string coin, deposithex; std::vector pubkeys,publishers; int64_t totalsupply,depositamount,tmpamount,inputs,CCchange=0; + int32_t numvouts,claimvout,height; std::vector proof; + uint256 hashBlock,tokenid,oracletxid,tmptxid,cointxid; char depositaddr[64],coinaddr[64],destaddr[64]; std::vector txids; + + cp = CCinit(&C,EVAL_GATEWAYS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); + gatewayspk = GetUnspendable(cp,0); + if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find bindtxid " << bindtxid.GetHex()); + if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid coin - bindtxid " << bindtxid.GetHex() << " coin." << coin); + if (komodo_txnotarizedconfirmed(bindtxid)==false) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "gatewaysbind tx not yet confirmed/notarized"); + if ( myGetTransaction(deposittxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find deposittxid " << bindtxid.GetHex()); + if (DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,tmptxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,tmpdestpub,tmpamount) != 'D' || coin != refcoin) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid coin - deposittxid " << bindtxid.GetHex() << " coin." << coin); + if (komodo_txnotarizedconfirmed(deposittxid)==false) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "gatewaysdeposit tx not yet confirmed/notarized"); + if (tmpdestpub!=destpub) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "different destination pubkey from desdeposit tx"); + if ( (depositamount=GatewaysDepositval(tx,mypk)) != amount ) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid Gateways deposittxid " << deposittxid.GetHex() << " " << (double)depositamount/COIN << " != " << (double)amount/COIN << ", remember claim must be done from destination pubkey from deposit tx!"); + if ((inputs=AddGatewaysInputs(cp, mtx, gatewayspk, bindtxid, amount, 60)) > 0) + { + if ( inputs > amount ) CCchange = (inputs - amount); + mtx.vin.push_back(CTxIn(deposittxid,0,CScript())); + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,amount,destpub)); + if ( CCchange != 0 ) mtx.vout.push_back(MakeTokensCC1vout(EVAL_GATEWAYS,CCchange,gatewayspk)); + return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysClaimOpRet('C',tokenid,bindtxid,refcoin,deposittxid,destpub,amount))); } - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cant find enough inputs"); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find enough tokens in gateways address for given amount"); } UniValue GatewaysWithdraw(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction tx; CPubKey mypk,gatewayspk,signerpk,tmpwithdrawpub; uint256 txid,tokenid,hashBlock,oracletxid,tmptokenid,tmpbindtxid,withdrawtxid; int32_t vout,numvouts,n,i; - int64_t nValue,totalsupply,inputs,CCchange=0,tmpamount,balance; uint8_t funcid,K,M,N,taddr,prefix,prefix2,wiftype; std::string coin,hex; + CTransaction tx; CPubKey mypk,gatewayspk,signerpk; uint256 txid,tokenid,hashBlock,oracletxid,tmptokenid,tmpbindtxid,withdrawtxid; int32_t vout,numvouts; + int64_t nValue,totalsupply,inputs,CCchange=0,tmpamount; uint8_t funcid,K,M,N,taddr,prefix,prefix2,wiftype; std::string coin,hex; std::vector pubkeys; char depositaddr[64],coinaddr[64]; struct CCcontract_info *cp,C,*cpTokens,CTokens; std::vector > unspentOutputs; cp = CCinit(&C,EVAL_GATEWAYS); cpTokens = CCinit(&CTokens,EVAL_TOKENS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_GATEWAYS]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); gatewayspk = GetUnspendable(cp, 0); if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cant find bindtxid " << bindtxid.GetHex()); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find bindtxid " << bindtxid.GetHex()); if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "invalid coin - bindtxid " << bindtxid.GetHex() << " coin." << coin); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid coin - bindtxid " << bindtxid.GetHex() << " coin." << coin); if (komodo_txnotarizedconfirmed(bindtxid)==false) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "gatewaysbind tx not yet confirmed/notarized"); - if (!withdrawpub.IsFullyValid()) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "withdraw destination pubkey is invalid"); - n = (int32_t)pubkeys.size(); - for (i=0; i balance ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "withdraw amount is not possible, deposit balance is lower than the amount!"); - if( AddNormalinputs(mtx, mypk, txfee+CC_MARKER_VALUE, 2,pk.IsValid()) >= txfee+CC_MARKER_VALUE ) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "gatewaysbind tx not yet confirmed/notarized"); + _GetCCaddress(coinaddr,EVAL_GATEWAYS,gatewayspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, amount, 60)) >= amount) + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + K=0; + if ( vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size())>0 && + (funcid=DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='P')) + { + if (funcid=='W' && DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,coin,withdrawpub,tmpamount)=='W' + && refcoin==coin && tmptokenid==tokenid && tmpbindtxid==bindtxid) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "unable to create withdraw, another withdraw pending"); + else if (funcid=='P' && DecodeGatewaysPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,hex)=='P' && + myGetTransaction(withdrawtxid,tx,hashBlock)!=0 && (numvouts=tx.vout.size())>0 && DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,coin,withdrawpub,tmpamount)=='W' + && refcoin==coin && tmptokenid==tokenid && tmpbindtxid==bindtxid) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "unable to create withdraw, another withdraw pending"); + } + } + if( AddNormalinputs(mtx, mypk, txfee+CC_MARKER_VALUE, 2,pk.IsValid()) > 0 ) + { + if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, amount, 60)) > 0) { if ( inputs > amount ) CCchange = (inputs - amount); mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk)); mtx.vout.push_back(MakeTokensCC1vout(EVAL_GATEWAYS,amount,gatewayspk)); if ( CCchange != 0 ) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); - return(FinalizeCCTxExt(pk.IsValid(),0, cpTokens, mtx, mypk, txfee,EncodeGatewaysWithdrawOpRet('W',tokenid,bindtxid,mypk,refcoin,withdrawpub,amount))); + return(FinalizeCCTxExt(pk.IsValid(),0, cpTokens, mtx, mypk, txfee,EncodeGatewaysWithdrawOpRet('W',tokenid,bindtxid,refcoin,withdrawpub,amount))); } - else CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "not enough balance of tokens for withdraw"); + else + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "not enough balance of tokens for withdraw"); } - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cant find enough normal inputs"); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find enough normal inputs"); } -UniValue GatewaysWithdrawSign(const CPubKey& pk, uint64_t txfee,uint256 lasttxid,std::string refcoin, std::string hex) +UniValue GatewaysPartialSign(const CPubKey& pk, uint64_t txfee,uint256 lasttxid,std::string refcoin, std::string hex) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,tmppk,gatewayspk,withdrawpub; struct CCcontract_info *cp,C; char funcid,depositaddr[64]; int64_t amount; - std::string coin,tmphex; CTransaction tx,tmptx; uint256 withdrawtxid,tmplasttxid,tokenid,tmptokenid,hashBlock,bindtxid,oracletxid; int32_t numvouts; - uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys,signingpubkeys; int64_t totalsupply; + CPubKey mypk,withdrawpub,signerpk,gatewayspk; struct CCcontract_info *cp,C; CTransaction tx,tmptx; + std::vector > unspentOutputs; char funcid,depositaddr[64]; + int32_t numvouts; uint256 withdrawtxid,hashBlock,bindtxid,tokenid,oracletxid,tmptokenid; std::string coin,tmphex; int64_t amount,totalsupply; + uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; cp = CCinit(&C,EVAL_GATEWAYS); + if ( txfee == 0 ) + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); gatewayspk = GetUnspendable(cp,0); - if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_GATEWAYS]?0:CC_TXFEE; if (myGetTransaction(lasttxid,tx,hashBlock)==0 || (numvouts= tx.vout.size())<=0 - || (funcid=DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='S')) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "invalid last txid" << lasttxid.GetHex()); + || (funcid=DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='P')) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "can't find last tx " << lasttxid.GetHex()); if (funcid=='W') { withdrawtxid=lasttxid; - if (DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmppk,coin,withdrawpub,amount)!='W' || refcoin!=coin) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cannot decode withdraw tx opret " << lasttxid.GetHex()); + if (DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid withdraw tx " << lasttxid.GetHex()); + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "gatewayswithdraw tx not yet confirmed/notarized"); + else if (myGetTransaction(bindtxid,tmptx,hashBlock)==0 || (numvouts=tmptx.vout.size())<=0) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "can't find bind tx " << bindtxid.GetHex()); + else if (DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin || tokenid!=tmptokenid) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid bind tx " << bindtxid.GetHex()); + } + else if (funcid=='P') + { + if (DecodeGatewaysPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,tmphex)!='P' || refcoin!=coin) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cannot decode partialsign tx opret " << lasttxid.GetHex()); + else if (myGetTransaction(withdrawtxid,tmptx,hashBlock)==0 || (numvouts= tmptx.vout.size())<=0) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "can't find withdraw tx " << withdrawtxid.GetHex()); + else if (DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,coin,withdrawpub,amount)!='W' + || refcoin!=coin) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid withdraw tx " << withdrawtxid.GetHex()); + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "gatewayswithdraw tx not yet confirmed/notarized"); else if (myGetTransaction(bindtxid,tmptx,hashBlock)==0 || (numvouts=tmptx.vout.size())<=0) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "can't find bind tx " << bindtxid.GetHex()); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "can't find bind tx " << bindtxid.GetHex()); + else if (DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin || tokenid!=tmptokenid) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid bind tx " << bindtxid.GetHex()); + } + if (AddNormalinputs(mtx,mypk,txfee,1,pk.IsValid())!=0) + { + mtx.vin.push_back(CTxIn(tx.GetHash(),0,CScript())); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk)); + return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysPartialOpRet('P',withdrawtxid,refcoin,K+1,mypk,hex))); + } + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "error adding funds for partialsign"); +} + +UniValue GatewaysCompleteSigning(const CPubKey& pk, uint64_t txfee,uint256 lasttxid,std::string refcoin,std::string hex) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,gatewayspk,signerpk,withdrawpub; struct CCcontract_info *cp,C; char funcid,depositaddr[64]; int64_t amount,totalsupply; + std::string coin,tmphex; CTransaction tx,tmptx; uint256 withdrawtxid,hashBlock,tokenid,tmptokenid,bindtxid,oracletxid; int32_t numvouts; + uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + + cp = CCinit(&C,EVAL_GATEWAYS); + mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); + gatewayspk = GetUnspendable(cp,0); + if ( txfee == 0 ) + txfee = 10000; + if (myGetTransaction(lasttxid,tx,hashBlock)==0 || (numvouts= tx.vout.size())<=0 + || (funcid=DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='P')) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid last txid " << lasttxid.GetHex()); + if (funcid=='W') + { + withdrawtxid=lasttxid; + if (DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid withdraw tx " << lasttxid.GetHex()); else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "gatewayswithdraw tx not yet confirmed/notarized"); - else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "can't find bind tx " << bindtxid.GetHex()); - else if (DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin!=coin || tokenid!=tmptokenid) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "invalid bind tx "<=txfee) + if (AddNormalinputs(mtx,mypk,txfee,1,pk.IsValid())!=0) { mtx.vin.push_back(CTxIn(lasttxid,0,CScript())); - mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk)); - signingpubkeys.push_back(mypk); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysWithdrawSignOpRet('S',withdrawtxid,lasttxid,signingpubkeys,refcoin,K+1,hex))); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk)); + return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysCompleteSigningOpRet('S',withdrawtxid,refcoin,K+1,hex))); } - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "error adding funds for withdrawsign"); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "error adding funds for completesigning"); } -UniValue GatewaysMarkDone(const CPubKey& pk, uint64_t txfee,uint256 withdrawsigntxid,std::string refcoin) +UniValue GatewaysMarkDone(const CPubKey& pk, uint64_t txfee,uint256 completetxid,std::string refcoin) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,tmppk; struct CCcontract_info *cp,C; char depositaddr[64]; CTransaction tx; int32_t numvouts; - uint256 withdrawtxid,tmplasttxid,tokenid,tmptokenid,bindtxid,oracletxid,hashBlock; std::string coin,hex; - uint8_t K,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys,signingpubkeys; int64_t amount,totalsupply; CPubKey withdrawpub; + CPubKey mypk; struct CCcontract_info *cp,C; char depositaddr[64]; CTransaction tx; int32_t numvouts; + uint256 withdrawtxid,bindtxid,oracletxid,tokenid,tmptokenid,hashBlock; std::string coin,hex; + uint8_t K,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; int64_t amount,totalsupply; CPubKey withdrawpub; cp = CCinit(&C,EVAL_GATEWAYS); - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); + mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_GATEWAYS]?0:CC_TXFEE; - if (myGetTransaction(withdrawsigntxid,tx,hashBlock)==0 || (numvouts= tx.vout.size())<=0) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "invalid withdrawsign txid " << withdrawsigntxid.GetHex()); - else if (DecodeGatewaysWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)!='S' || refcoin!=coin) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cannot decode withdrawsign tx opret " << withdrawsigntxid.GetHex()); + txfee = 10000; + if (myGetTransaction(completetxid,tx,hashBlock)==0 || (numvouts= tx.vout.size())<=0) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid completesigning txid " << completetxid.GetHex()); + else if (DecodeGatewaysCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,hex)!='S' || refcoin!=coin) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cannot decode completesigning tx opret " << completetxid.GetHex()); + if (komodo_txnotarizedconfirmed(completetxid)==false) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "gatewayscompletesigning tx not yet confirmed/notarized"); else if (myGetTransaction(withdrawtxid,tx,hashBlock)==0 || (numvouts= tx.vout.size())==0) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "invalid withdraw txid " << withdrawtxid.GetHex()); - else if (DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmppk,coin,withdrawpub,amount)!='W' || refcoin!=coin) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cannot decode withdraw tx opret " << withdrawtxid.GetHex()); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid withdraw txid " << withdrawtxid.GetHex()); + else if (DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cannot decode withdraw tx opret " << withdrawtxid.GetHex()); else if (myGetTransaction(bindtxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "can't find bind tx " << bindtxid.GetHex()); - else if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin!=coin || tokenid!=tmptokenid) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "invalid bind tx "<< bindtxid.GetHex()); - if (AddNormalinputs(mtx,mypk,txfee,3)>=txfee) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "can't find bind tx " << bindtxid.GetHex()); + else if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin || tokenid!=tmptokenid) + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid bind tx " << bindtxid.GetHex()); + if (AddNormalinputs(mtx,mypk,txfee,1,pk.IsValid())!=0) { - mtx.vin.push_back(CTxIn(withdrawsigntxid,0,CScript())); + mtx.vin.push_back(CTxIn(completetxid,0,CScript())); mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysMarkDoneOpRet('M',withdrawtxid,mypk,refcoin,withdrawsigntxid))); + return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysMarkDoneOpRet('M',withdrawtxid,refcoin,completetxid))); } - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "error adding funds for markdone"); + CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "error adding funds for markdone"); } UniValue GatewaysPendingDeposits(const CPubKey& pk, uint256 bindtxid,std::string refcoin) @@ -1085,7 +1249,7 @@ UniValue GatewaysPendingDeposits(const CPubKey& pk, uint256 bindtxid,std::string vout = (int32_t)it->first.index; nValue = (int64_t)it->second.satoshis; if ( vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts=tx.vout.size())>0 && - DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmpbindtxid,coin,publishers,txids,height,cointxid,claimvout,hex,proof,destpub,amount) == 'D' + DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,publishers,txids,height,cointxid,claimvout,hex,proof,destpub,amount) == 'D' && tmpbindtxid==bindtxid && refcoin == coin && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) { UniValue obj(UniValue::VOBJ); @@ -1108,22 +1272,31 @@ UniValue GatewaysPendingDeposits(const CPubKey& pk, uint256 bindtxid,std::string return(result); } -UniValue GatewaysPendingSignWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin) +UniValue GatewaysPendingWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin) { - UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx,withdrawtx; std::string coin,hex; CPubKey mypk,tmppk,gatewayspk,withdrawpub; - std::vector msigpubkeys; uint256 hashBlock,txid,tmpbindtxid,tokenid,tmptokenid,oracletxid,withdrawtxid,tmplasttxid; uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; - char funcid,gatewaystokensaddr[65],str[65],depositaddr[65],coinaddr[65],destaddr[65],withaddr[65],numstr[32],signeraddr[65],txidaddr[65]; - int32_t i,n,numvouts,vout,queueflag; int64_t amount,nValue,totalsupply; struct CCcontract_info *cp,C; std::vector signingpubkeys; - std::vector > unspentOutputs; std::vector txs; std::vector tmp_txs; + UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx; std::string coin,hex; CPubKey mypk,gatewayspk,withdrawpub,signerpk; + std::vector msigpubkeys; uint256 hashBlock,tokenid,txid,tmpbindtxid,tmptokenid,oracletxid,withdrawtxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; + char funcid,depositaddr[65],coinaddr[65],tokensaddr[65],destaddr[65],str[65],withaddr[65],numstr[32],signeraddr[65],txidaddr[65]; + int32_t i,n,numvouts,vout,queueflag; int64_t totalsupply,amount,nValue; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; cp = CCinit(&C,EVAL_GATEWAYS); mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); gatewayspk = GetUnspendable(cp,0); _GetCCaddress(coinaddr,EVAL_GATEWAYS,gatewayspk); + GetTokensCCaddress(cp,tokensaddr,gatewayspk); if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cant find bindtxid " << bindtxid.GetHex()); + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); + return(result); + } if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "invalid bindtxid " << bindtxid.GetHex() << " coin." << coin); + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); + return(result); + } n = msigpubkeys.size(); queueflag = 0; for (i=0; i::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++) - { - tx = *it; - vout=0; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,tx.GetHash(),vout)==0 && (numvouts= tx.vout.size())>0 && tx.vout[vout].nValue == CC_MARKER_VALUE && - DecodeGatewaysWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)=='S' && myGetTransaction(withdrawtxid,withdrawtx,hashBlock)!=0 - && (numvouts=withdrawtx.vout.size())>0 && DecodeGatewaysWithdrawOpRet(withdrawtx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,tmppk,coin,withdrawpub,amount)=='W' - && refcoin==coin && tmpbindtxid==bindtxid && tmptokenid==tokenid) - { - txs.push_back(tx); - break; - break; - break; - } - } - if (txs.empty()) - { - SetCCunspents(unspentOutputs,coinaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - nValue = (int64_t)it->second.satoshis; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 && vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && - (numvouts= tx.vout.size())>0 && DecodeGatewaysWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)=='S' && myGetTransaction(withdrawtxid,withdrawtx,hashBlock)!=0 - && (numvouts=withdrawtx.vout.size())>0 && DecodeGatewaysWithdrawOpRet(withdrawtx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,tmppk,coin,withdrawpub,amount)=='W' && refcoin==coin && tmpbindtxid==bindtxid && tmptokenid==tokenid) - { - txs.push_back(tx); - break; - } - } - } - if (txs.empty()) - { - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - nValue = (int64_t)it->second.satoshis; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 && vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && - (numvouts= tx.vout.size())>0 && DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,tmppk,coin,withdrawpub,amount)=='W' && - tmpbindtxid==bindtxid && tmptokenid==tokenid && komodo_get_blocktime(hashBlock)+3600>GetTime()) - { - txs.push_back(tx); - break; - } - } - } - for (std::vector::const_iterator it=txs.begin(); it!=txs.end(); it++) + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - tx = *it; - vout=0; + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; K=0; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,tx.GetHash(),vout) == 0 && tx.vout[vout].nValue == CC_MARKER_VALUE && (numvouts= tx.vout.size())>0 && (funcid=DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='S')) + if ( vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size())>0 && + (funcid=DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='P') && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) { - txid=tx.GetHash(); if (funcid=='W') { - DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,tmppk,coin,withdrawpub,amount); - withdrawtxid=txid; + if (DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,coin,withdrawpub,amount)==0 || refcoin!=coin || tmptokenid!=tokenid || tmpbindtxid!=bindtxid) continue; } - else if (funcid=='S') + else if (funcid=='P') { - DecodeGatewaysWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex); - if (myGetTransaction(withdrawtxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0 || DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,tmppk,coin,withdrawpub,amount)!='W') continue; - } + if (DecodeGatewaysPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,hex)!='P' || myGetTransaction(withdrawtxid,tx,hashBlock)==0 + || (numvouts=tx.vout.size())<=0 || DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,coin,withdrawpub,amount)!='W' + || refcoin!=coin || tmptokenid!=tokenid || tmpbindtxid!=bindtxid) + continue; + } Getscriptaddress(destaddr,tx.vout[1].scriptPubKey); GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); - GetTokensCCaddress(cp,gatewaystokensaddr,gatewayspk); - if ( strcmp(destaddr,gatewaystokensaddr) == 0 ) + if ( strcmp(destaddr,tokensaddr) == 0 ) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("withdrawtxid",withdrawtxid.GetHex())); - CCCustomtxidaddr(txidaddr,withdrawtxid,taddr,prefix,prefix2); + obj.push_back(Pair("withdrawtxid",uint256_str(str,tx.GetHash()))); + CCCustomtxidaddr(txidaddr,tx.GetHash(),taddr,prefix,prefix2); obj.push_back(Pair("withdrawtxidaddr",txidaddr)); obj.push_back(Pair("withdrawaddr",withaddr)); sprintf(numstr,"%.8f",(double)tx.vout[1].nValue/COIN); obj.push_back(Pair("amount",numstr)); - obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(withdrawtxid))); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(tx.GetHash()))); if ( queueflag != 0 ) { obj.push_back(Pair("depositaddr",depositaddr)); GetCustomscriptaddress(signeraddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG,taddr,prefix,prefix2); obj.push_back(Pair("signeraddr",signeraddr)); } - obj.push_back(Pair("last_txid",txid.GetHex())); - if (funcid=='S' && (std::find(signingpubkeys.begin(),signingpubkeys.end(),mypk)!=signingpubkeys.end() || K>=M) ) obj.push_back(Pair("processed",true)); if (N>1) { obj.push_back(Pair("number_of_signs",K)); + obj.push_back(Pair("last_txid",uint256_str(str,txid))); if (K>0) obj.push_back(Pair("hex",hex)); } pending.push_back(obj); @@ -1235,23 +1360,31 @@ UniValue GatewaysPendingSignWithdraws(const CPubKey& pk, uint256 bindtxid,std::s return(result); } -UniValue GatewaysSignedWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin) +UniValue GatewaysProcessedWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin) { - UniValue result(UniValue::VOBJ),processed(UniValue::VARR); CTransaction tx,tmptx,withdrawtx; std::string coin,hex; - CPubKey mypk,tmppk,gatewayspk,withdrawpub; std::vector msigpubkeys; - uint256 withdrawtxid,tmplasttxid,tmpbindtxid,tokenid,tmptokenid,hashBlock,txid,oracletxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; - char depositaddr[65],signeraddr[65],coinaddr[65],numstr[32],withaddr[65],txidaddr[65]; - int32_t i,n,numvouts,vout,queueflag; int64_t nValue,amount,totalsupply; struct CCcontract_info *cp,C; std::vector signingpubkeys; - std::vector > unspentOutputs; std::vector txs; std::vector tmp_txs; + UniValue result(UniValue::VOBJ),processed(UniValue::VARR); CTransaction tx; std::string coin,hex; + CPubKey mypk,gatewayspk,withdrawpub; std::vector msigpubkeys; + uint256 withdrawtxid,hashBlock,txid,tokenid,tmptokenid,oracletxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; + char depositaddr[65],coinaddr[65],str[65],numstr[32],withaddr[65],txidaddr[65]; + int32_t i,n,numvouts,vout,queueflag; int64_t totalsupply,nValue,amount; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; cp = CCinit(&C,EVAL_GATEWAYS); mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); gatewayspk = GetUnspendable(cp,0); _GetCCaddress(coinaddr,EVAL_GATEWAYS,gatewayspk); if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "cant find bindtxid " << bindtxid.GetHex()); + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); + return(result); + } if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin) - CCERR_RESULT("gatewayscc",CCLOG_ERROR, stream << "invalid bindtxid " << bindtxid.GetHex() << " coin." << coin); + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); + return(result); + } n = msigpubkeys.size(); queueflag = 0; for (i=0; i::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++) - { - tx = *it; - vout=0; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,tx.GetHash(),vout) == 0 && (numvouts=tx.vout.size())>0 && tx.vout[vout].nValue == CC_MARKER_VALUE && - DecodeGatewaysWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)=='S' && K>=M && refcoin==coin && - myGetTransaction(withdrawtxid,withdrawtx,hashBlock) != 0 && (numvouts= withdrawtx.vout.size())>0 && - DecodeGatewaysWithdrawOpRet(withdrawtx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,tmppk,coin,withdrawpub,amount) == 'W' && tmpbindtxid==bindtxid && tmptokenid==tokenid) - txs.push_back(tx); - } SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; nValue = (int64_t)it->second.satoshis; - if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 && vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size())>0 && - DecodeGatewaysWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)=='S' && K>=M && refcoin==coin && - myGetTransaction(withdrawtxid,withdrawtx,hashBlock) != 0 && (numvouts= withdrawtx.vout.size())>0 && - DecodeGatewaysWithdrawOpRet(withdrawtx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,tmppk,coin,withdrawpub,amount) == 'W' && tmpbindtxid==bindtxid && tmptokenid==tokenid) - txs.push_back(tx); - } - for (std::vector::const_iterator it=txs.begin(); it!=txs.end(); it++) - { - tx = *it; - vout =0; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,tx.GetHash(),vout) == 0 && (numvouts=tx.vout.size())>0 && DecodeGatewaysWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)=='S' && - K>=M && myGetTransaction(withdrawtxid,withdrawtx,hashBlock) != 0 && (numvouts= withdrawtx.vout.size())>0 && DecodeGatewaysWithdrawOpRet(withdrawtx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,tmppk,coin,withdrawpub,amount) == 'W') - { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("withdrawsigntxid",tx.GetHash().GetHex())); - obj.push_back(Pair("withdrawtxid",withdrawtxid.GetHex())); - CCCustomtxidaddr(txidaddr,withdrawtxid,taddr,prefix,prefix2); - obj.push_back(Pair("withdrawtxidaddr",txidaddr)); - GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); - obj.push_back(Pair("withdrawaddr",withaddr)); - obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(txid))); - DecodeHexTx(tmptx,hex); - sprintf(numstr,"%.8f",(double)tmptx.vout[0].nValue/COIN); - obj.push_back(Pair("amount",numstr)); - if ( queueflag != 0 ) + if ( vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size())>0 && + DecodeGatewaysCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,hex) == 'S' && refcoin == coin && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) + { + if (myGetTransaction(withdrawtxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size())>0 + && DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,coin,withdrawpub,amount) == 'W' || refcoin!=coin || tmptokenid!=tokenid) { - obj.push_back(Pair("depositaddr",depositaddr)); - GetCustomscriptaddress(signeraddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG,taddr,prefix,prefix2); - obj.push_back(Pair("signeraddr",signeraddr)); + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("completesigningtxid",uint256_str(str,txid))); + obj.push_back(Pair("withdrawtxid",uint256_str(str,withdrawtxid))); + CCCustomtxidaddr(txidaddr,withdrawtxid,taddr,prefix,prefix2); + obj.push_back(Pair("withdrawtxidaddr",txidaddr)); + GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); + obj.push_back(Pair("withdrawaddr",withaddr)); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(txid))); + sprintf(numstr,"%.8f",(double)tx.vout[1].nValue/COIN); + obj.push_back(Pair("amount",numstr)); + obj.push_back(Pair("hex",hex)); + processed.push_back(obj); } - obj.push_back(Pair("last_txid",tx.GetHash().GetHex())); - if (N>1) obj.push_back(Pair("number_of_signs",K)); - if (K>0) obj.push_back(Pair("hex",hex)); - processed.push_back(obj); } } result.push_back(Pair("coin",refcoin)); - result.push_back(Pair("signed",processed)); + result.push_back(Pair("processed",processed)); result.push_back(Pair("queueflag",queueflag)); return(result); } @@ -1323,7 +1430,7 @@ UniValue GatewaysList() { UniValue result(UniValue::VARR); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid,tokenid; CTransaction vintx; std::string coin; int64_t totalsupply; char str[65],depositaddr[64]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; cp = CCinit(&C,EVAL_GATEWAYS); - SetCCtxids(txids,cp->unspendableCCaddr,true,EVAL_GATEWAYS,CC_MARKER_VALUE,zeroid,'B'); + SetCCtxids(txids,cp->unspendableCCaddr,true,EVAL_GATEWAYS,zeroid,'B'); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { txid = *it; @@ -1427,7 +1534,7 @@ UniValue GatewaysInfo(uint256 bindtxid) result.push_back(Pair("prefix",prefix)); result.push_back(Pair("prefix2",prefix2)); result.push_back(Pair("wiftype",wiftype)); - result.push_back(Pair("depositaddr",depositaddr)); + result.push_back(Pair("deposit",depositaddr)); result.push_back(Pair("tokenid",uint256_str(str,tokenid))); sprintf(numstr,"%.8f",(double)totalsupply/COIN); result.push_back(Pair("totalsupply",numstr)); diff --git a/src/cc/heir.cpp b/src/cc/heir.cpp index cd42499bb47..728d82f2697 100644 --- a/src/cc/heir.cpp +++ b/src/cc/heir.cpp @@ -15,44 +15,11 @@ #include "CCHeir.h" #include "heir_validate.h" -#include "old/heir_validate_v0.h" #include class CoinHelper; class TokenHelper; -#ifndef MAY2020_NNELECTION_HARDFORK -#define MAY2020_NNELECTION_HARDFORK 1590926400 -#endif - -// return true if new v1 version activation time is passed or chain is always works v1 -// return false if v0 is still active -bool HeirIsVer1Active(const Eval *eval) -{ - static const char *chains_only_version1[] = { - // "RFOXLIKE", - // "DIMXY11", - // "DIMXY14", "DIMXY14_2" - "HEIR1" - }; - - bool isTimev1 = true; - if (eval == NULL) { - // std::cerr << __func__ << " komodo_currentheight()=" << komodo_currentheight() << " GetLatestTimestamp(komodo_currentheight())=" << GetLatestTimestamp(komodo_currentheight()) << std::endl; - if (GetLatestTimestamp(komodo_currentheight()) < MAY2020_NNELECTION_HARDFORK) - isTimev1 = false; - } - else { - // std::cerr << __func__ << " eval->GetCurrentHeight()=" << eval->GetCurrentHeight() << " GetLatestTimestamp(eval->GetCurrentHeight())=" << GetLatestTimestamp(eval->GetCurrentHeight()) << std::endl; - if (GetLatestTimestamp(eval->GetCurrentHeight()) < MAY2020_NNELECTION_HARDFORK) - isTimev1 = false; - } - for (auto const name : chains_only_version1) - if (strcmp(name, ASSETCHAINS_SYMBOL) == 0) - return true; - return isTimev1; -} - /* The idea of Heir CC is to allow crypto inheritance. A special 1of2 CC address is created that is freely spendable by the creator (funds owner). @@ -161,7 +128,7 @@ bool HeirValidate(struct CCcontract_info* cpHeir, Eval* eval, const CTransaction uint8_t hasHeirSpendingBegun = 0, hasHeirSpendingBegunDummy; CScript opret = (tx.vout.size() > 0) ? tx.vout[tx.vout.size() - 1].scriptPubKey : CScript(); // check boundary - uint8_t funcId = DecodeHeirEitherOpRetV1(opret, tokenidThis, fundingTxidInOpret, hasHeirSpendingBegunDummy, true); + uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenidThis, fundingTxidInOpret, hasHeirSpendingBegunDummy, true); if (funcId == 0) return eval->Invalid("invalid opreturn format"); @@ -257,7 +224,7 @@ bool HeirValidate(struct CCcontract_info* cpHeir, Eval* eval, const CTransaction // break; default: - LOGSTREAMFN("heir", CCLOG_INFO, stream << "illegal heir funcid=" << (char)funcId << std::endl); + std::cerr << "HeirValidate() illegal heir funcid=" << (char)funcId << std::endl; return eval->Invalid("unexpected HeirValidate funcid"); // break; } @@ -272,56 +239,55 @@ bool HeirValidate(struct CCcontract_info* cpHeir, Eval* eval, const CTransaction * Checks if vout is to cryptocondition address * @return vout value in satoshis */ -/* not used, there is IsTokenVout used for tokens case template int64_t IsHeirFundingVout(struct CCcontract_info* cp, const CTransaction& tx, int32_t voutIndex, CPubKey ownerPubkey, CPubKey heirPubkey) { - char destaddr[KOMODO_ADDRESS_BUFSIZE], heirFundingAddr[KOMODO_ADDRESS_BUFSIZE]; + char destaddr[65], heirFundingAddr[65]; Helper::GetCoinsOrTokensCCaddress1of2(cp, heirFundingAddr, ownerPubkey, heirPubkey); - if (tx.vout[voutIndex].scriptPubKey.IsPayToCryptoCondition() != false) { + if (tx.vout[voutIndex].scriptPubKey.IsPayToCryptoCondition() != 0) { + // NOTE: dimxy it was unsafe 'Getscriptaddress(destaddr,tx.vout[voutIndex].scriptPubKey) > 0' here: if (Getscriptaddress(destaddr, tx.vout[voutIndex].scriptPubKey) && strcmp(destaddr, heirFundingAddr) == 0) return (tx.vout[voutIndex].nValue); else - LOGSTREAMFN("heir", CCLOG_DEBUG1, stream << "heirFundingAddr=" << heirFundingAddr << " not equal to destaddr=" << destaddr << std::endl); + std::cerr << "IsHeirFundingVout() heirFundingAddr=" << heirFundingAddr << " not equal to destaddr=" << destaddr << std::endl; } return (0); } -*/ // makes coin initial tx opret -vscript_t EncodeHeirCreateOpRetV1(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) +vscript_t EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) { uint8_t evalcode = EVAL_HEIR; - uint8_t version = 1; - return E_MARSHAL(ss << evalcode << funcid << version << ownerPubkey << heirPubkey << inactivityTimeSec << heirName << memo); + return /*CScript() << OP_RETURN <<*/ E_MARSHAL(ss << evalcode << funcid << ownerPubkey << heirPubkey << inactivityTimeSec << heirName << memo); } // makes coin additional tx opret -vscript_t EncodeHeirOpRetV1(uint8_t funcid, uint256 fundingtxid, uint8_t hasHeirSpendingBegun) +vscript_t EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t hasHeirSpendingBegun) { uint8_t evalcode = EVAL_HEIR; - uint8_t version = 1; fundingtxid = revuint256(fundingtxid); - return E_MARSHAL(ss << evalcode << funcid << version << fundingtxid << hasHeirSpendingBegun); + return /*CScript() << OP_RETURN <<*/ E_MARSHAL(ss << evalcode << funcid << fundingtxid << hasHeirSpendingBegun); } // decode opret vout for Heir contract -uint8_t _DecodeHeirOpRetV1(vscript_t vopret, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) -{ +uint8_t _DecodeHeirOpRet(vscript_t vopret, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) +{ + uint8_t evalCodeInOpret = 0; + uint8_t heirFuncId = 0; + fundingTxidInOpret = zeroid; //to init - if (vopret.size() >= 3 && vopret[0] == EVAL_HEIR) - { - uint8_t evalCodeInOpret = 0; + evalCodeInOpret = vopret.begin()[0]; + + if (vopret.size() > 1 && evalCodeInOpret == EVAL_HEIR) { // NOTE: it unmarshals for all F, A and C uint8_t heirFuncId = 0; hasHeirSpendingBegun = 0; - uint8_t version; - bool result = E_UNMARSHAL(vopret, { ss >> evalCodeInOpret; ss >> heirFuncId; ss >> version; + bool result = E_UNMARSHAL(vopret, { ss >> evalCodeInOpret; ss >> heirFuncId; if (heirFuncId == 'F') { ss >> ownerPubkey; ss >> heirPubkey; ss >> inactivityTime; ss >> heirName; ss >> memo; } @@ -331,41 +297,52 @@ uint8_t _DecodeHeirOpRetV1(vscript_t vopret, CPubKey& ownerPubkey, CPubKey& heir }); if (!result) { - LOGSTREAMFN("heir", CCLOG_DEBUG1, stream << "could not unmarshal opret, evalCode=" << (int)evalCodeInOpret << std::endl); + if (!noLogging) std::cerr << "_DecodeHeirOpRet() could not unmarshal opret, evalCode=" << (int)evalCodeInOpret << std::endl; return (uint8_t)0; } + /* std::cerr << "DecodeHeirOpRet()" + << " heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') + << " ownerPubkey=" << HexStr(ownerPubkey) + << " heirPubkey=" << HexStr(heirPubkey) + << " heirName=" << heirName << " inactivityTime=" << inactivityTime + << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << std::endl; */ + if (isMyFuncId(heirFuncId)) { fundingTxidInOpret = revuint256(fundingTxidInOpret); return heirFuncId; } else { - LOGSTREAMFN("heir", CCLOG_DEBUG1, stream << "unexpected opret type, heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') << std::endl); + if(!noLogging) std::cerr << "_DecodeHeirOpRet() unexpected opret type, heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') << std::endl; } } else { - LOGSTREAMFN("heir", CCLOG_DEBUG1, stream << "not a heir opret, vopretExtra.size() < 3 or not EVAL_HEIR evalcode=" << (int)(vopret.size() > 0 ? vopret[0] : 0) << std::endl); + if (!noLogging) std::cerr << "_DecodeHeirOpRet() not a heir opret, vopretExtra.size() == 0 or not EVAL_HEIR evalcode=" << (int)evalCodeInOpret << std::endl; } return (uint8_t)0; } // decode combined opret: -uint8_t _DecodeHeirEitherOpRetV1(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) +uint8_t _DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { uint8_t evalCodeTokens = 0; std::vector voutPubkeysDummy; - std::vector oprets; + std::vector> oprets; vscript_t vopretExtra /*, vopretStripped*/; - // try to parse - uint8_t oldfuncid; - if ((oldfuncid = heirv0::_DecodeHeirEitherOpRet(scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, fundingTxidInOpret, hasHeirSpendingBegun, noLogging)) != 0) - return oldfuncid; - if (DecodeTokenOpRetV1(scriptPubKey, tokenid, voutPubkeysDummy, oprets) != 0 && GetOpReturnCCBlob(oprets, vopretExtra)) - { + if (DecodeTokenOpRet(scriptPubKey, evalCodeTokens, tokenid, voutPubkeysDummy, oprets) != 0 && GetOpretBlob(oprets, OPRETID_HEIRDATA, vopretExtra)) { + /* if (vopretExtra.size() > 1) { + // restore the second opret: + + /* unmarshalled in DecodeTokenOpRet: + if (!E_UNMARSHAL(vopretExtra, { ss >> vopretStripped; })) { //strip string size + if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() could not unmarshal vopretStripped" << std::endl; + return (uint8_t)0; + } + } */ if (vopretExtra.size() < 1) { - LOGSTREAMFN("heir", CCLOG_DEBUG1, stream << " empty vopretExtra" << std::endl); + if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() empty vopretExtra" << std::endl; return (uint8_t)0; } } @@ -373,24 +350,41 @@ uint8_t _DecodeHeirEitherOpRetV1(CScript scriptPubKey, uint256 &tokenid, CPubKey GetOpReturnData(scriptPubKey, vopretExtra); } - return _DecodeHeirOpRetV1(vopretExtra, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); + return _DecodeHeirOpRet(vopretExtra, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); } // overload to decode opret in fundingtxid: -uint8_t DecodeHeirEitherOpRetV1(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging) { +uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging) { uint256 dummyFundingTxidInOpret; uint8_t dummyHasHeirSpendingBegun; - return _DecodeHeirEitherOpRetV1(scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, dummyFundingTxidInOpret, dummyHasHeirSpendingBegun, noLogging); + return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, dummyFundingTxidInOpret, dummyHasHeirSpendingBegun, noLogging); } // overload to decode opret in A and C heir tx: -uint8_t DecodeHeirEitherOpRetV1(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { +uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { CPubKey dummyOwnerPubkey, dummyHeirPubkey; int64_t dummyInactivityTime; std::string dummyHeirName, dummyMemo; - return _DecodeHeirEitherOpRetV1(scriptPubKey, tokenid, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, dummyMemo, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); + return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, dummyMemo, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); +} + +// check if pubkey is in vins +void CheckVinPubkey(std::vector vins, CPubKey pubkey, bool &hasPubkey, bool &hasOtherPubkey) { + + hasPubkey = false; + hasOtherPubkey = false; + + for (auto vin : vins) { + CPubKey vinPubkey = check_signing_pubkey(vin.scriptSig); + if (vinPubkey.IsValid()) { + if (vinPubkey == pubkey) + hasPubkey = true; + if (vinPubkey != pubkey) + hasOtherPubkey = true; + } + } } /** @@ -403,8 +397,9 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke uint256 hashBlock; const bool allowSlow = false; - if (!HeirIsVer1Active(NULL)) - return heirv0::_FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, fundingOpretScript, hasHeirSpendingBegun); + //char markeraddr[64]; + //CCtxidaddr(markeraddr, fundingtxid); + //SetCCunspents(unspentOutputs, markeraddr,true); hasHeirSpendingBegun = 0; funcId = 0; @@ -413,32 +408,29 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { CScript heirScript = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); - uint8_t funcId = DecodeHeirEitherOpRetV1(heirScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); + uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); if (funcId != 0) { // found at least funding tx! //std::cerr << "FindLatestFundingTx() lasttx currently is fundingtx, txid=" << fundingtxid.GetHex() << " opreturn type=" << (char)funcId << '\n'; fundingOpretScript = fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey; } else { - LOGSTREAMFN("heir", CCLOG_DEBUG1, stream << "could not decode opreturn for fundingtxid=" << fundingtxid.GetHex() << '\n'); + std::cerr << "FindLatestFundingTx() could not decode opreturn for fundingtxid=" << fundingtxid.GetHex() << '\n'; return zeroid; } } else { - LOGSTREAMFN("heir", CCLOG_DEBUG1, stream << "could not find funding tx for fundingtxid=" << fundingtxid.GetHex() << '\n'); + std::cerr << "FindLatestFundingTx() could not find funding tx for fundingtxid=" << fundingtxid.GetHex() << '\n'; return zeroid; } // TODO: correct cc addr: std::vector> unspentOutputs; - - char coinaddr[KOMODO_ADDRESS_BUFSIZE]; - if (tokenid.IsNull()) - CoinHelper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); - else - TokenHelper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); + struct CCcontract_info *cp, C; + cp = CCinit(&C, EVAL_HEIR); + char coinaddr[64]; + GetCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' - SetCCunspents(unspentOutputs, coinaddr, true); // get vector with tx's with unspent vouts of 1of2pubkey address: - // std::cerr << __func__ << " unspents size=" << unspentOutputs.size() << '\n'; - + SetCCunspents(unspentOutputs, coinaddr,true); // get vector with tx's with unspent vouts of 1of2pubkey address: + //std::cerr << "FindLatestFundingTx() using 1of2address=" << coinaddr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; int32_t maxBlockHeight = 0; // max block height uint256 latesttxid = fundingtxid; @@ -449,20 +441,20 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke uint256 hash; uint256 txid = it->first.txhash; - // std::cerr << __func__ << " checking unspents for txid=" << txid.GetHex() << '\n'; + //std::cerr << "FindLatestFundingTx() checking unspents for txid=" << txid.GetHex() << '\n'; int32_t blockHeight = (int32_t)it->second.blockHeight; //NOTE: maybe called from validation code: if (myGetTransaction(txid, regtx, hash)) { - // std::cerr << __func__ << " found tx for txid=" << txid.GetHex() << " blockHeight=" << blockHeight << " maxBlockHeight=" << maxBlockHeight << '\n'; + //std::cerr << "FindLatestFundingTx() found tx for txid=" << txid.GetHex() << " blockHeight=" << blockHeight << " maxBlockHeight=" << maxBlockHeight << '\n'; uint256 fundingTxidInOpret; uint256 tokenidInOpret; // not to contaminate the tokenid from the params! uint8_t tmpFuncId; uint8_t hasHeirSpendingBegunInOpret; CScript heirScript = (regtx.vout.size() > 0) ? regtx.vout[regtx.vout.size() - 1].scriptPubKey : CScript(); - tmpFuncId = DecodeHeirEitherOpRetV1(heirScript, tokenidInOpret, fundingTxidInOpret, hasHeirSpendingBegunInOpret, true); + tmpFuncId = DecodeHeirEitherOpRet(heirScript, tokenidInOpret, fundingTxidInOpret, hasHeirSpendingBegunInOpret, true); if (tmpFuncId != 0 && fundingtxid == fundingTxidInOpret && (tokenid == zeroid || tokenid == tokenidInOpret)) { // check tokenid also if (blockHeight > maxBlockHeight) { @@ -471,17 +463,18 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke bool isOwner = false; bool isNonOwner = false; + CheckVinPubkey(regtx.vin, ownerPubkey, isOwner, isNonOwner); + // we ignore 'donations' tx (with non-owner inputs) for calculating if heir is allowed to spend: - if (TotalPubkeyNormalInputs(regtx, ownerPubkey) > 0 || TotalPubkeyCCInputs(regtx, ownerPubkey) > 0) - { - // CheckVinPubkey(regtx.vin, ownerPubkey, isOwner, isNonOwner); - // if (isOwner && !isNonOwner) { + if (isOwner && !isNonOwner) { hasHeirSpendingBegun = hasHeirSpendingBegunInOpret; maxBlockHeight = blockHeight; latesttxid = txid; funcId = tmpFuncId; - } - // std::cerr << __func__ << " txid=" << latesttxid.GetHex() << " at blockHeight=" << maxBlockHeight << " opreturn type=" << (char)(funcId ? funcId : ' ') << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << " - set as current lasttxid" << '\n'; + } + + //std::cerr << "FindLatestFundingTx() txid=" << latesttxid.GetHex() << " at blockHeight=" << maxBlockHeight + // << " opreturn type=" << (char)(funcId ? funcId : ' ') << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << " - set as current lasttxid" << '\n'; } } } @@ -522,21 +515,29 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, char coinaddr[64]; Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); // get address of cryptocondition '1 of 2 pubkeys' SetCCunspents(unspentOutputs, coinaddr,true); - + + // char markeraddr[64]; + // CCtxidaddr(markeraddr, fundingtxid); + // SetCCunspents(unspentOutputs, markeraddr,true); + + std::cerr << "Add1of2AddressInputs() using 1of2addr=" << coinaddr << " unspentOutputs.size()=" << unspentOutputs.size() << std::endl; + for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { uint256 txid = it->first.txhash; uint256 hashBlock; int32_t voutIndex = (int32_t)it->first.index; // no need to prevent dup // dimxy: maybe it is good to put tx's in cache? - + + //std::cerr << "Add1of2AddressInputs() txid=" << txid.GetHex() << std::endl; + if (myGetTransaction(txid, heirtx, hashBlock) != 0) { uint256 tokenid; uint256 fundingTxidInOpret; uint8_t hasHeirSpendingBegunDummy; CScript heirScript = (heirtx.vout.size() > 0) ? heirtx.vout[heirtx.vout.size() - 1].scriptPubKey : CScript(); // check boundary - uint8_t funcId = DecodeHeirEitherOpRetV1(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); + uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); if ((txid == fundingtxid || fundingTxidInOpret == fundingtxid) && funcId != 0 && @@ -545,6 +546,7 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, //(voutValue = IsHeirFundingVout(cp, heirtx, voutIndex, ownerPubkey, heirPubkey)) > 0 && // heir contract vout validation logic - not used since we moved to 2-eval vouts !myIsutxo_spentinmempool(ignoretxid,ignorevin,txid, voutIndex)) { + std::cerr << "Add1of2AddressInputs() satoshis=" << it->second.satoshis << std::endl; if (total != 0 && maxinputs != 0) mtx.vin.push_back(CTxIn(txid, voutIndex, CScript())); nValue = it->second.satoshis; @@ -569,6 +571,7 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info std::vector> addressIndexes; SetCCtxids(addressIndexes, coinaddr,true); + //fprintf(stderr,"LifetimeHeirContractFunds() scan lifetime of %s\n",coinaddr); int64_t total = 0; for (std::vector>::const_iterator it = addressIndexes.begin(); it != addressIndexes.end(); it++) { uint256 hashBlock; @@ -583,9 +586,9 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info const int32_t ivout = 0; CScript heirScript = (heirtx.vout.size() > 0) ? heirtx.vout[heirtx.vout.size() - 1].scriptPubKey : CScript(); // check boundary - uint8_t funcId = DecodeHeirEitherOpRetV1(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); + uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); - //std::cerr << __func__ << " found tx=" << txid.GetHex() << " vout[0].nValue=" << subtx.vout[ccVoutIdx].nValue << " opreturn=" << (char)funcId << '\n'; + //std::cerr << "LifetimeHeirContractFunds() found tx=" << txid.GetHex() << " vout[0].nValue=" << subtx.vout[ccVoutIdx].nValue << " opreturn=" << (char)funcId << '\n'; if (funcId != 0 && (txid == fundingtxid || fundingTxidInOpret == fundingtxid) && @@ -594,7 +597,7 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info !myIsutxo_spentinmempool(ignoretxid,ignorevin,txid, ivout)) // exclude tx in mempool { total += it->second; // dont do this: tx.vout[ivout].nValue; // in vin[0] always is the pay to 1of2 addr (funding or change) - //std::cerr << __func__ << " added tx=" << txid.GetHex() << " it->second=" << it->second << " vout[0].nValue=" << tx.vout[ivout].nValue << " opreturn=" << (char)funcId << '\n'; + //std::cerr << "LifetimeHeirContractFunds() added tx=" << txid.GetHex() << " it->second=" << it->second << " vout[0].nValue=" << tx.vout[ivout].nValue << " opreturn=" << (char)funcId << '\n'; } } } @@ -620,13 +623,16 @@ template UniValue _HeirFund(int64_t txfee, int64_t amount, std txfee = 10000; int64_t markerfee = 10000; - + + //std::cerr << "HeirFund() amount=" << amount << " txfee=" << txfee << " heirPubkey IsValid()=" << heirPubkey.IsValid() << " inactivityTime(sec)=" << inactivityTimeSec << " tokenid=" << tokenid.GetHex() << std::endl; + CPubKey myPubkey = pubkey2pk(Mypubkey()); if (!tokenid.IsNull()) // add normals only for tokens { - if (AddNormalinputs(mtx, myPubkey, txfee + markerfee, 0x10000) < txfee + markerfee) + if (AddNormalinputs(mtx, myPubkey, txfee + markerfee, 4) < txfee + markerfee) { + std::cerr << "HeirFund() could not find normal inputs for txfee" << std::endl; result.push_back(Pair("result", "error")); result.push_back(Pair("error", "could not find normal inputs for txfee")); return result; @@ -635,7 +641,7 @@ template UniValue _HeirFund(int64_t txfee, int64_t amount, std int64_t inputs; int64_t addAmount = tokenid.IsNull() ? (txfee + markerfee + amount) : amount; // for coins add txfee markerfee amount in one call - if ((inputs=Helper::addOwnerInputs(tokenid, mtx, myPubkey, addAmount, (int32_t)0x10000)) >= addAmount) + if ((inputs=Helper::addOwnerInputs(tokenid, mtx, myPubkey, addAmount, (int32_t)64)) >= addAmount) { mtx.vout.push_back(Helper::make1of2Vout(amount, myPubkey, heirPubkey)); @@ -661,15 +667,13 @@ template UniValue _HeirFund(int64_t txfee, int64_t amount, std } // check owner pubkey in vins - // bool hasMypubkey = false; - // bool hasNotMypubkey = false; + bool hasMypubkey = false; + bool hasNotMypubkey = false; - // CheckVinPubkey(mtx.vin, myPubkey, hasMypubkey, hasNotMypubkey); + CheckVinPubkey(mtx.vin, myPubkey, hasMypubkey, hasNotMypubkey); // for initial funding do not allow to sign by non-owner key: - // if (hasNotMypubkey) { - if (TotalPubkeyNormalInputs(mtx, myPubkey) < amount && TotalPubkeyCCInputs(mtx, myPubkey) < amount) - { + if (hasNotMypubkey) { result.push_back(Pair("result", "error")); result.push_back(Pair("error", "using non-owner inputs not allowed")); return result; @@ -740,8 +744,9 @@ template UniValue _HeirAdd(uint256 fundingtxid, int64_t txfee, in if (!tokenid.IsNull()) // add normals only for tokens { - if (AddNormalinputs(mtx, myPubkey, txfee + markerfee, 0x10000) < txfee + markerfee) + if (AddNormalinputs(mtx, myPubkey, txfee + markerfee, 4) < txfee + markerfee) { + std::cerr << "HeirFund() could not find normal inputs for txfee" << std::endl; result.push_back(Pair("result", "error")); result.push_back(Pair("error", "could not find normal inputs for txfee")); return result; @@ -750,7 +755,7 @@ template UniValue _HeirAdd(uint256 fundingtxid, int64_t txfee, in int64_t inputs; int64_t addAmount = tokenid.IsNull() ? (txfee + markerfee + amount) : amount; // for coins add txfee markerfee amount in one call - if ((inputs = Helper::addOwnerInputs(tokenid, mtx, myPubkey, addAmount, 0x10000)) >= addAmount) { // TODO: why 64 max inputs? + if ((inputs = Helper::addOwnerInputs(tokenid, mtx, myPubkey, addAmount, 64)) >= addAmount) { // TODO: why 64 max inputs? // we do not use markers anymore - storing data in opreturn is better // add marker vout: @@ -778,7 +783,6 @@ template UniValue _HeirAdd(uint256 fundingtxid, int64_t txfee, in mtx.vout.push_back(Helper::makeUserVout(ccChange, myPubkey)); } - /* CheckVinPubkey does not work for normals, switched to TotalPubkeyNormalInputs and TotalPubkeyCCInputs // check owner pubkey in vins bool hasMypubkey = false; bool hasNotMypubkey = false; @@ -790,11 +794,10 @@ template UniValue _HeirAdd(uint256 fundingtxid, int64_t txfee, in result.push_back(Pair("result", "error")); result.push_back(Pair("error", "using both owner and non-owner inputs is not allowed")); return result; - }*/ + } // warn the user he's making a donation if this is all non-owner keys: - // if (hasNotMypubkey) { - if (TotalPubkeyNormalInputs(mtx, myPubkey) < amount && TotalPubkeyCCInputs(mtx, myPubkey) < amount) { + if (hasNotMypubkey) { result.push_back(Pair("result", "warning")); result.push_back(Pair("warning", "you are about to make a donation to heir fund")); } @@ -887,13 +890,9 @@ template UniValue _HeirClaim(uint256 fundingtxid, int64_t txfee CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CPubKey myPubkey; int64_t inputs, change = 0; - - struct CCcontract_info *cp, C, *cpTokens, CTokens; + struct CCcontract_info *cp, C; + cp = CCinit(&C, EVAL_HEIR); - - if (!tokenid.IsNull()) - cpTokens = CCinit(&CTokens, EVAL_TOKENS); - if (txfee == 0) txfee = 10000; @@ -903,7 +902,7 @@ template UniValue _HeirClaim(uint256 fundingtxid, int64_t txfee // we do not need to find duration if spending already has begun if (!hasHeirSpendingBegun) { durationSec = CCduration(numblocks, latesttxid); - // std::cerr << "HeirClaim() duration=" << durationSec << " inactivityTime=" << inactivityTimeSec << " numblocks=" << numblocks << std::endl; + std::cerr << "HeirClaim() duration=" << durationSec << " inactivityTime=" << inactivityTimeSec << " numblocks=" << numblocks << std::endl; } // spending is allowed if there is already spending tx or inactivity time @@ -927,10 +926,10 @@ template UniValue _HeirClaim(uint256 fundingtxid, int64_t txfee } */ // add spending txfee from the calling user - if (AddNormalinputs(mtx, myPubkey, txfee, 0x10000) > 0) { + if (AddNormalinputs(mtx, myPubkey, txfee, 3) > 0) { // add spending from cc 1of2 address - if ((inputs = Add1of2AddressInputs(tokenid.IsNull() ? cp : cpTokens, fundingtxid, mtx, ownerPubkey, heirPubkey, amount, 0x10000)) >= amount) // TODO: why only 60 inputs? + if ((inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, amount, 60)) >= amount) // TODO: why only 60 inputs? { /*if (inputs < amount) { std::cerr << "HeirClaim() cant find enough HeirCC 1of2 inputs, found=" << inputs << " required=" << amount << std::endl; @@ -987,15 +986,18 @@ template UniValue _HeirClaim(uint256 fundingtxid, int64_t txfee result.push_back(Pair("hex", rawhextx)); } else { + std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; result.push_back(Pair("result", "error")); result.push_back(Pair("error", "sign error")); } } else { + fprintf(stderr, "HeirClaim() cant find Heir CC inputs\n"); result.push_back(Pair("result", "error")); result.push_back(Pair("error", "can't find heir CC funding")); } } else { + fprintf(stderr, "HeirClaim() cant find sufficient user inputs for tx fee\n"); result.push_back(Pair("result", "error")); result.push_back(Pair("error", "can't find sufficient user inputs to pay transaction fee")); } @@ -1043,6 +1045,7 @@ UniValue HeirClaimCaller(uint256 fundingtxid, int64_t txfee, std::string strAmou else { UniValue result(UniValue::VOBJ); + fprintf(stderr, "HeirClaim() can't find any heir CC funding tx's\n"); result.push_back(Pair("result", "error")); result.push_back(Pair("error", "can't find any heir CC funding transactions")); return result; @@ -1065,6 +1068,9 @@ UniValue HeirInfo(uint256 fundingtxid) uint256 hashBlock; const bool allowSlow = false; + //char markeraddr[64]; + //CCtxidaddr(markeraddr, fundingtxid); + //SetCCunspents(unspentOutputs, markeraddr,true); // get initial funding tx and set it as initial lasttx: if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { @@ -1076,6 +1082,18 @@ UniValue HeirInfo(uint256 fundingtxid) const bool noLogging = false; uint8_t funcId; + /*CScript opret = fundingtx.vout.size() > 0 ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); + uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, true); + if (funcId == 0) { + std::cerr << "HeirInfo() this fundingtx is incorrect" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "initial tx F not found")); + return result; + }*/ + + struct CCcontract_info *cp, C; + cp = CCinit(&C, EVAL_HEIR); + uint8_t hasHeirSpendingBegun = 0; uint256 latestFundingTxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo, hasHeirSpendingBegun); @@ -1084,11 +1102,13 @@ UniValue HeirInfo(uint256 fundingtxid) int32_t numblocks; uint64_t durationSec = 0; - std::cerr << __func__ << " latesttxid=" << latestFundingTxid.GetHex() << '\n'; + //std::cerr << "HeirInfo() latesttxid=" << latestFundingTxid.GetHex() << '\n'; std::ostringstream stream; std::string msg; + //sleep(10); + result.push_back(Pair("fundingtxid", fundingtxid.GetHex())); result.push_back(Pair("name", heirName.c_str())); @@ -1110,12 +1130,6 @@ UniValue HeirInfo(uint256 fundingtxid) result.push_back(Pair("heir", stream.str().c_str())); stream.str(""); stream.clear(); - - struct CCcontract_info *cp, C; - if (tokenid.IsNull()) - cp = CCinit(&C, EVAL_HEIR); - else - cp = CCinit(&C, EVAL_TOKENS); int64_t total; if (tokenid == zeroid) @@ -1163,7 +1177,7 @@ UniValue HeirInfo(uint256 fundingtxid) stream.clear(); if (tokenid != zeroid) { - int64_t ownerInputs = TokenHelper::addOwnerInputs(tokenid, mtx, ownerPubkey, 0, (int32_t)0x10000); + int64_t ownerInputs = TokenHelper::addOwnerInputs(tokenid, mtx, ownerPubkey, 0, (int32_t)64); stream << ownerInputs; msg = "OwnerRemainderTokens"; result.push_back(Pair(msg, stream.str().c_str())); @@ -1175,12 +1189,10 @@ UniValue HeirInfo(uint256 fundingtxid) result.push_back(Pair("InactivityTimeSetting", stream.str().c_str())); stream.str(""); stream.clear(); - - result.push_back(Pair("lasttxid", latestFundingTxid.GetHex())); if (!hasHeirSpendingBegun) { // we do not need find duration if the spending already has begun durationSec = CCduration(numblocks, latestFundingTxid); - // std::cerr << __func__ << " duration (sec)=" << durationSec << " inactivityTime (sec)=" << inactivityTimeSec << " numblocks=" << numblocks << '\n'; + std::cerr << "HeirInfo() duration (sec)=" << durationSec << " inactivityTime (sec)=" << inactivityTimeSec << " numblocks=" << numblocks << '\n'; } stream << std::boolalpha << (hasHeirSpendingBegun || durationSec > inactivityTimeSec); @@ -1224,14 +1236,18 @@ void _HeirList(struct CCcontract_info *cp, UniValue &result) GetCCaddress(cp, markeraddr, GetUnspendable(cp, NULL)); SetCCunspents(unspentOutputs, markeraddr,true); - + + //std::cerr << "HeirList() finding heir marker from unspendable addr=" << markeraddr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; + // TODO: move marker to special cc addr to prevent checking all tokens for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { uint256 hashBlock; uint256 txid = it->first.txhash; uint256 tokenid; int32_t vout = (int32_t)it->first.index; - + + //std::cerr << "HeirList() checking txid=" << txid.GetHex() << " vout=" << vout << '\n'; + CTransaction fundingtx; if (myGetTransaction(txid, fundingtx, hashBlock)) { CPubKey ownerPubkey, heirPubkey; @@ -1241,18 +1257,19 @@ void _HeirList(struct CCcontract_info *cp, UniValue &result) uint256 tokenid; CScript opret = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); - uint8_t funcId = DecodeHeirEitherOpRetV1(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo, true); + uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo, true); // note: if it is not Heir token funcId would be equal to 0 if (funcId == 'F') { + //result.push_back(Pair("fundingtxid kind name", txid.GetHex() + std::string(" ") + (typeid(Helper) == typeid(TokenHelper) ? std::string("token") : std::string("coin")) + std::string(" ") + heirName)); result.push_back( txid.GetHex() ); } else { - std::cerr << __func__ << " this is not the initial F transaction=" << txid.GetHex() << std::endl; + std::cerr << "HeirList() this is not the initial F transaction=" << txid.GetHex() << std::endl; } } else { - std::cerr << __func__ << " could not load transaction=" << txid.GetHex() << std::endl; + std::cerr << "HeirList() could not load transaction=" << txid.GetHex() << std::endl; } } } diff --git a/src/cc/heir_validate.h b/src/cc/heir_validate.h index 2762996c5c1..768390aa3c5 100644 --- a/src/cc/heir_validate.h +++ b/src/cc/heir_validate.h @@ -3,20 +3,18 @@ #include "CCinclude.h" #include "CCHeir.h" -#include "old/heir_validate_v0.h" - #define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) -bool HeirIsVer1Active(const Eval *eval); - // makes coin initial tx opret -vscript_t EncodeHeirCreateOpRetV1(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo); -vscript_t EncodeHeirOpRetV1(uint8_t funcid, uint256 fundingtxid, uint8_t isHeirSpendingBegan); +vscript_t EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo); +vscript_t EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t isHeirSpendingBegan); uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &isHeirSpendingBegan); -uint8_t DecodeHeirEitherOpRetV1(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging = false); -uint8_t DecodeHeirEitherOpRetV1(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging = false); +//uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxid, uint8_t &isHeirSpendingBegan, bool noLogging = false); +//uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false); +uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging = false); +uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging = false); inline static bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); } inline static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); } @@ -27,27 +25,17 @@ class CoinHelper { static uint8_t getMyEval() { return EVAL_HEIR; } static int64_t addOwnerInputs(uint256 dummyid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { - return AddNormalinputsRemote(mtx, ownerPubkey, total, maxinputs); + return AddNormalinputs(mtx, ownerPubkey, total, maxinputs); } static CScript makeCreateOpRet(uint256 dummyid, std::vector dummyPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) { - if (HeirIsVer1Active(NULL)) - return CScript() << OP_RETURN << EncodeHeirCreateOpRetV1((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo); - else - return CScript() << OP_RETURN << heirv0::EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo); + return CScript() << OP_RETURN << EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo); } static CScript makeAddOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - if (HeirIsVer1Active(NULL)) - return CScript() << OP_RETURN << EncodeHeirOpRetV1((uint8_t)'A', fundingtxid, isHeirSpendingBegan); - else - return CScript() << OP_RETURN << heirv0::EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); - + return CScript() << OP_RETURN << EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); } static CScript makeClaimOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - if (HeirIsVer1Active(NULL)) - return CScript() << OP_RETURN << EncodeHeirOpRetV1((uint8_t)'C', fundingtxid, isHeirSpendingBegan); - else - return CScript() << OP_RETURN << heirv0::EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan); + return CScript() << OP_RETURN << EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan); } static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { return MakeCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); @@ -77,28 +65,22 @@ class TokenHelper { public: static uint8_t getMyEval() { return EVAL_TOKENS; } static int64_t addOwnerInputs(uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { - struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - return AddTokenCCInputs(cpTokens, mtx, ownerPubkey, tokenid, total, maxinputs); + struct CCcontract_info *cpHeir, heirC; + cpHeir = CCinit(&heirC, EVAL_TOKENS); + return AddTokenCCInputs(cpHeir, mtx, ownerPubkey, tokenid, total, maxinputs); } static CScript makeCreateOpRet(uint256 tokenid, std::vector voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) { - if (HeirIsVer1Active(NULL)) - return EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, { EncodeHeirCreateOpRetV1((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo) }); - else - return EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, { heirv0::EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo) }); + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + std::make_pair(OPRETID_HEIRDATA, EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo))); } static CScript makeAddOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - if (HeirIsVer1Active(NULL)) - return EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, { EncodeHeirOpRetV1((uint8_t)'A', fundingtxid, isHeirSpendingBegan) }); - else - return EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, { heirv0::EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan) }); + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan))); } static CScript makeClaimOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - if (HeirIsVer1Active(NULL)) - return EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, { EncodeHeirOpRetV1((uint8_t)'C', fundingtxid, isHeirSpendingBegan) }); - else - return EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, { heirv0::EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan) }); + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan))); } static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { @@ -434,7 +416,7 @@ template class CCC1of2AddressValidator : CValidatorBase std::string heirName, memo; uint256 tokenid; - uint8_t funcId = DecodeHeirEitherOpRetV1(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); if (funcId == 0) { message = m_customMessage + std::string(" invalid opreturn format"); std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; @@ -492,7 +474,7 @@ template class CMyPubkeyVoutValidator : CValidatorBase ///std::cerr << "CMyPubkeyVoutValidator::validateVout() m_opRetScript=" << m_opRetScript.ToString() << std::endl; // get both pubkeys: - uint8_t funcId = DecodeHeirEitherOpRetV1(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); if (funcId == 0) { message = std::string("invalid opreturn format"); return false; @@ -550,7 +532,7 @@ template class CHeirSpendValidator : CValidatorBase uint256 tokenid; // get heir pubkey: - uint8_t funcId = DecodeHeirEitherOpRetV1(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); if (funcId == 0) { message = std::string("invalid opreturn format"); return false; @@ -603,13 +585,13 @@ template class COpRetValidator : CValidatorBase uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid; uint8_t dummyIsHeirSpendingBegan; - uint8_t funcId = DecodeHeirEitherOpRetV1(vout.scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); + uint8_t funcId = DecodeHeirEitherOpRet(vout.scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); if (funcId == 0) { message = std::string("invalid opreturn format"); return false; } - uint8_t initialFuncId = DecodeHeirEitherOpRetV1(m_fundingOpretScript, initialTokenid, dummyTxid, dummyIsHeirSpendingBegan, true); + uint8_t initialFuncId = DecodeHeirEitherOpRet(m_fundingOpretScript, initialTokenid, dummyTxid, dummyIsHeirSpendingBegan, true); if (initialFuncId == 0) { message = std::string("invalid initial tx opreturn format"); return false; @@ -659,7 +641,7 @@ template class CMarkerValidator : CValidatorBase if (prevVout.size() > 0) { // get funcId for prev tx: - uint8_t funcId = DecodeHeirEitherOpRetV1(prevVout[prevVout.size()-1].scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); + uint8_t funcId = DecodeHeirEitherOpRet(prevVout[prevVout.size()-1].scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); //std::cerr << "CMarkerValidator::validateVin() funcId=" << (funcId?funcId:' ') << std::endl; diff --git a/src/cc/import.cpp b/src/cc/import.cpp index 3b9c007c9b1..4a1978e8d6a 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -24,8 +24,6 @@ #include "key_io.h" #define CODA_BURN_ADDRESS "KPrrRoPfHOnNpZZQ6laHXdQDkSQDkVHaN0V+LizLlHxz7NaA59sBAAAA" -#define CC_MARKER_VALUE 1000 -#define PEGS_ACCOUNT_MAX_DEBT 80 /* * CC Eval method for import coin. * @@ -46,8 +44,6 @@ uint256 GatewaysReverseScan(uint256 &txid, int32_t height, uint256 reforacletxid int32_t GatewaysCointxidExists(struct CCcontract_info *cp, uint256 cointxid); uint8_t DecodeImportGatewayBindOpRet(char *burnaddr,const CScript &scriptPubKey,std::string &coin,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &importgatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype); int64_t ImportGatewayVerify(char *refburnaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 burntxid,const std::string deposithex,std::vectorproof,uint256 merkleroot,CPubKey destpub,uint8_t taddr,uint8_t prefix,uint8_t prefix2); -char PegsFindAccount(struct CCcontract_info *cp,CPubKey pk,uint256 pegstxid, uint256 tokenid, uint256 &accounttxid, std::pair &account); -double PegsGetRatio(uint256 tokenid,std::pair account); char *nonportable_path(char *str); char *portable_path(char *str); void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep); @@ -86,7 +82,7 @@ CMutableTransaction MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount) CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); if (AddNormalinputs(mtx, myPubKey, 2 * txfee, 4) == 0) { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "MakeSelfImportSourceTx() warning: cannot find normal inputs for txfee" << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeSelfImportSourceTx() warning: cannot find normal inputs for txfee" << std::endl); } CScript scriptPubKey = GetScriptForDestination(dest); @@ -108,7 +104,7 @@ bool CheckVinPubKey(const CTransaction &sourcetx, int32_t i, uint8_t pubkey33[33 return false; if( !myGetTransaction(sourcetx.vin[i].prevout.hash, vintx, blockHash) ) { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "CheckVinPubKey() could not load vintx" << sourcetx.vin[i].prevout.hash.GetHex() << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVinPubKey() could not load vintx" << sourcetx.vin[i].prevout.hash.GetHex() << std::endl); return false; } if( sourcetx.vin[i].prevout.n < vintx.vout.size() && Getscriptaddress(destaddr, vintx.vout[sourcetx.vin[i].prevout.n].scriptPubKey) != 0 ) @@ -117,7 +113,7 @@ bool CheckVinPubKey(const CTransaction &sourcetx, int32_t i, uint8_t pubkey33[33 if (strcmp(pkaddr, destaddr) == 0) { return true; } - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "CheckVinPubKey() mismatched vin[" << i << "].prevout.n=" << sourcetx.vin[i].prevout.n << " -> destaddr=" << destaddr << " vs pkaddr=" << pkaddr << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVinPubKey() mismatched vin[" << i << "].prevout.n=" << sourcetx.vin[i].prevout.n << " -> destaddr=" << destaddr << " vs pkaddr=" << pkaddr << std::endl); } return false; } @@ -134,12 +130,12 @@ int32_t GetSelfimportProof(const CMutableTransaction sourceMtx, CMutableTransact /* if (!E_UNMARSHAL(ParseHex(rawsourcetx), ss >> sourcetx)) { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "GetSelfimportProof: could not unmarshal source tx" << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: could not unmarshal source tx" << std::endl); return(-1); } if (sourcetx.vout.size() == 0) { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "GetSelfimportProof: vout size is 0" << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: vout size is 0" << std::endl); return -1; } */ @@ -153,7 +149,7 @@ int32_t GetSelfimportProof(const CMutableTransaction sourceMtx, CMutableTransact } if (ivout >= sourcetx.vout.size()) { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "GetSelfimportProof: needed vout not found" << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: needed vout not found" << std::endl); return -1; } */ @@ -181,7 +177,7 @@ int32_t GetSelfimportProof(const CMutableTransaction sourceMtx, CMutableTransact vscript_t vopret; if( !GetOpReturnData(sourceMtx.vout.back().scriptPubKey, vopret) || !E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> burnAmount)) { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "GetSelfimportProof() could not unmarshal source tx opret" << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof() could not unmarshal source tx opret" << std::endl); return -1; } templateMtx.vout[0].nValue = burnAmount; @@ -189,7 +185,7 @@ int32_t GetSelfimportProof(const CMutableTransaction sourceMtx, CMutableTransact // not sure we need this now as we create sourcetx ourselves: /*if (sourcetx.GetHash() != sourcetxid) { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "GetSelfimportProof: passed source txid incorrect" << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: passed source txid incorrect" << std::endl); return(-1); }*/ @@ -208,7 +204,7 @@ std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string sr CPubKey mypk; uint256 codaburntxid; std::vector dummyproof; int32_t i,numvouts,n,m; std::string coin,error; struct CCcontract_info *cp, C; cJSON *result,*tmp,*tmp1; unsigned char hash[SHA256_DIGEST_LENGTH+1]; - char out[SHA256_DIGEST_LENGTH*2+1],*retstr,*destaddr,*receiver; uint64_t amount; + char out[SHA256_DIGEST_LENGTH*2+1],*retstr,*destaddr,*receiver; TxProof txProof; uint64_t amount; cp = CCinit(&C, EVAL_GATEWAYS); if (txfee == 0) @@ -246,7 +242,7 @@ std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string sr { CCerror="MakeCodaImportTx: invalid burn address, coins do not go to predefined burn address - "; CCerror+=CODA_BURN_ADDRESS; - LOGSTREAM("importcoin", CCLOG_ERROR, stream << CCerror << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); free(result); return(""); } @@ -255,32 +251,32 @@ std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string sr if (vouts[0]!=CTxOut(amount*COIN,scriptPubKey)) { CCerror="MakeCodaImportTx: invalid destination address, burnTx memo!=importTx destination"; - LOGSTREAM("importcoin", CCLOG_ERROR, stream << CCerror << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); free(result); return(""); } if (amount*COIN!=vouts[0].nValue) { CCerror="MakeCodaImportTx: invalid amount, burnTx amount!=importTx amount"; - LOGSTREAM("importcoin", CCLOG_ERROR, stream << CCerror << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); free(result); return(""); } burntx.vin.push_back(CTxIn(codaburntxid,0,CScript())); burntx.vout.push_back(MakeBurnOutput(amount*COIN,0xffffffff,"CODA",vouts,dummyproof,srcaddr,receipt)); - return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(ImportProof(),burntx,vouts))); + return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof,burntx,vouts))); } else { CCerror="MakeCodaImportTx: invalid Coda burn tx"; - LOGSTREAM("importcoin", CCLOG_ERROR, stream << CCerror << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); free(result); return(""); } } CCerror="MakeCodaImportTx: error fetching Coda tx"; - LOGSTREAM("importcoin", CCLOG_ERROR, stream << CCerror << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); free(result); return(""); } @@ -311,7 +307,7 @@ int32_t CheckCODAimport(CTransaction importTx,CTransaction burnTx,std::vector account, CPubKey accountpk) -{ - struct CCcontract_info *cp,C; char addr[64]; CPubKey pegspk; uint256 prevaccounttxid; - std::pair prevaccount(0,0); - - cp = CCinit(&C,EVAL_PEGS); - pegspk=GetUnspendable(cp,0); - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - { - LOGSTREAM("pegscc", CCLOG_ERROR, stream << "invalid pegs txid. On this chain only valid pegs txid is " << KOMODO_EARLYTXID.GetHex() << std::endl); - return(-1); - } - else if ( (*cp->ismyvin)(importTx.vin[1].scriptSig) == 0 ) - { - LOGSTREAM("pegscc", CCLOG_ERROR, stream << "vin.1 is CC for pegsget!" << std::endl); - return(-1); - } - else if ( (*cp->ismyvin)(importTx.vin[2].scriptSig) == 0 ) - { - LOGSTREAM("pegscc", CCLOG_ERROR, stream << "vin.2 is CC for pegsget!" << std::endl); - return(-1); - } - else if ( GetCCaddress1of2(cp,addr,pegspk,pegspk) && ConstrainVout(importTx.vout[0],1,addr,CC_MARKER_VALUE)==0) - { - LOGSTREAM("pegscc", CCLOG_ERROR, stream << "invalid account marker vout.0 for pegsget!" << std::endl); - return(-1); - } - else if ( GetCCaddress1of2(cp,addr,srcpub,pegspk) && ConstrainVout(importTx.vout[1],1,addr,CC_MARKER_VALUE)==0) - { - LOGSTREAM("pegscc", CCLOG_ERROR, stream << "invalid account marker vout.1 for pegsget!" << std::endl); - return(-1); - } - else if (PegsFindAccount(cp,srcpub,pegstxid,tokenid,prevaccounttxid,prevaccount)==0) - { - LOGSTREAM("pegscc", CCLOG_ERROR, stream << "cannot find account from which to issue coins, fund account first with pegsfund!" << std::endl); - return(-1); - } - else if (prevaccounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,prevaccounttxid,1) != 0 && ignoretxid!=importTx.GetHash()) - { - fprintf(stderr,"%s %d %s\n",prevaccounttxid.GetHex().c_str(),myIsutxo_spentinmempool(ignoretxid,ignorevin,prevaccounttxid,1),ignoretxid.GetHex().c_str()); - LOGSTREAM("pegscc", CCLOG_ERROR, stream << "previous account tx not yet confirmed" << std::endl); - return(-1); - } -#ifndef TESTMODE_PEGS - else if (PegsGetRatio(tokenid,account)>PEGS_ACCOUNT_MAX_DEBT) - { - LOGSTREAM("pegscc", CCLOG_ERROR, stream << "not possible to take more than " << PEGS_ACCOUNT_MAX_DEBT << "%% of the deposit price value" << std::endl); - return(-1); - } -#else - else if (PegsGetRatio(tokenid,account)>100) - { - LOGSTREAM("pegscc", CCLOG_ERROR, stream << "not possible to take more than 100%% of the deposit price value" << std::endl); - return(-1); - } -#endif - else if ( Getscriptaddress(addr,CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG) && ConstrainVout(importTx.vout[2],0,addr,amount)==0) - { - LOGSTREAM("pegscc", CCLOG_ERROR, stream << "invalid destination or amount of coins issued with pegsget!" << std::endl); - return(-1); - } - else if (prevaccount.second+amount!=account.second || prevaccount.first!=account.first || srcpub!=accountpk) - { - fprintf(stderr,"%ld %ld %ld %ld %ld\n",prevaccount.second,amount,account.second,prevaccount.first,account.first); - LOGSTREAM("pegscc", CCLOG_ERROR, stream << "invalid previous and current account comparisons!" << std::endl); - return(-1); - } - return(0); -} - -int32_t CheckGATEWAYimport(CTransaction importTx,CTransaction deposittx,std::string refcoin,std::vector proof, - uint256 bindtxid,std::vector publishers,std::vector txids,uint256 deposittxid,int32_t height,int32_t burnvout,std::string rawdeposittx,CPubKey destpub, int64_t amount) +int32_t CheckGATEWAYimport(CTransaction importTx,CTransaction burnTx,std::string refcoin,std::vector proof, + uint256 bindtxid,std::vector publishers,std::vector txids,uint256 burntxid,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub, int64_t amount) { CTransaction oracletx,bindtx,regtx; int32_t i,m,n=0,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype; uint256 txid,oracletxid,tmporacletxid,merkleroot,mhash,hashBlock; @@ -437,58 +363,53 @@ int32_t CheckGATEWAYimport(CTransaction importTx,CTransaction deposittx,std::str // ASSETCHAINS_SELFIMPORT is coin if (KOMODO_EARLYTXID!=zeroid && bindtxid!=KOMODO_EARLYTXID) { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport invalid import gateway. On this chain only valid import gateway is " << KOMODO_EARLYTXID.GetHex() << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid import gateway. On this chain only valid import gateway is " << KOMODO_EARLYTXID.GetHex() << std::endl); return(-1); } // check for valid burn from external coin blockchain and if valid return(0); if (myGetTransaction(bindtxid, bindtx, hashBlock) == 0 || (numvouts = bindtx.vout.size()) <= 0) { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport cant find bindtxid=" << bindtxid.GetHex() << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport cant find bindtxid=" << bindtxid.GetHex() << std::endl); return(-1); } else if (DecodeImportGatewayBindOpRet(deposit,bindtx.vout[numvouts - 1].scriptPubKey,coin,oracletxid,M,N,tmppubkeys,taddr,prefix,prefix2,wiftype) != 'B') { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "CheckGATEWAYimport invalid bind tx. bindtxid=" << bindtxid.GetHex() << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid bind tx. bindtxid=" << bindtxid.GetHex() << std::endl); return(-1); } else if (refcoin!=coin) { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport invalid coin " << refcoin << "!=" << coin << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid coin " << refcoin << "!=" << coin << std::endl); return(-1); } else if ( N == 0 || N > 15 || M > N ) { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport invalid N or M " << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid N or M " << std::endl); return(-1); } else if (tmppubkeys.size()!=N) { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport not enough pubkeys for given N " << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport not enough pubkeys for given N " << std::endl); return(-1); } else if (komodo_txnotarizedconfirmed(bindtxid) == false) { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport bindtx not yet confirmed/notarized" << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport bindtx not yet confirmed/notarized" << std::endl); return(-1); } else if (myGetTransaction(oracletxid, oracletx, hashBlock) == 0 || (numvouts = oracletx.vout.size()) <= 0) { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport cant find oracletxid=" << oracletxid.GetHex() << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport cant find oracletxid=" << oracletxid.GetHex() << std::endl); return(-1); } else if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey,name,desc,format) != 'C') { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport invalid oracle tx. oracletxid=" << oracletxid.GetHex() << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle tx. oracletxid=" << oracletxid.GetHex() << std::endl); return(-1); } - else if (!destpub.IsFullyValid()) + else if (name!=refcoin || format!="Ihh") { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport invalid destination pubkey" << std::endl); - return(-1); - } - else if (name!=refcoin || format!="IhhL") - { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport invalid oracle name or format tx. oracletxid=" << oracletxid.GetHex() << " name=" << name << " format=" << format << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle name or format tx. oracletxid=" << oracletxid.GetHex() << " name=" << name << " format=" << format << std::endl); return(-1); } CCtxidaddr(markeraddr,oracletxid); @@ -497,19 +418,15 @@ int32_t CheckGATEWAYimport(CTransaction importTx,CTransaction deposittx,std::str { txid = it->first.txhash; if ( myGetTransaction(txid,regtx,hashBlock) != 0 && regtx.vout.size() > 0 - && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid) + && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid ) { - std::vector::iterator it1 = std::find(pubkeys.begin(), pubkeys.end(), regpk); - if (it1 == pubkeys.end()) - { - pubkeys.push_back(regpk); - n++; - } + pubkeys.push_back(regpk); + n++; } } if (pubkeys.size()!=tmppubkeys.size()) { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport different number of bind and oracle pubkeys " << tmppubkeys.size() << "!=" << pubkeys.size() << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport different number of bind and oracle pubkeys " << tmppubkeys.size() << "!=" << pubkeys.size() << std::endl); return(-1); } merkleroot = zeroid; @@ -526,30 +443,30 @@ int32_t CheckGATEWAYimport(CTransaction importTx,CTransaction deposittx,std::str } if (publishers.size()!=tmppublishers.size()) { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport different number of publishers for deposittx in oracle" << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport different number of publishers for burtx in oracle" << std::endl); return(-1); } else if (merkleroot == zeroid || m < n / 2) // none or less than half oracle nodes sent merkleroot { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport couldnt find merkleroot for block height=" << height << "coin=" << refcoin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl ); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport couldnt find merkleroot for block height=" << height << "coin=" << refcoin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl ); return(-1); } - else if ( ImportGatewayVerify(deposit,oracletxid,burnvout,refcoin,deposittxid,rawdeposittx,proof,merkleroot,destpub,taddr,prefix,prefix2) != amount ) + else if ( ImportGatewayVerify(deposit,oracletxid,burnvout,refcoin,burntxid,rawburntx,proof,merkleroot,destpub,taddr,prefix,prefix2) != amount ) { - CCerror = strprintf("deposittxid didnt validate !"); - LOGSTREAM("importgateway",CCLOG_ERROR, stream << CCerror << std::endl); + CCerror = strprintf("burntxid didnt validate !"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); return(-1); } else if (importTx.vout[0].nValue!=amount) { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport import amount different than in deposittx" << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport import amount different than in burntx" << std::endl); return(-1); } Getscriptaddress(destaddr,importTx.vout[0].scriptPubKey); Getscriptaddress(tmpdest,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG); if (strcmp(destaddr,tmpdest)!=0) { - LOGSTREAM("importgateway", CCLOG_ERROR, stream << "CheckGATEWAYimport import coins destination different than in deposittx" << std::endl); + LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport import coins destination different than in burntx" << std::endl); return(-1); } return(0); @@ -564,18 +481,18 @@ int32_t CheckPUBKEYimport(TxProof proof,std::vector rawproof,CTransacti CTransaction sourcetx; if (!myGetTransaction(sourcetxid, sourcetx, hashBlock)) { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "could not load source txid=" << sourcetxid.GetHex() << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "could not load source txid=" << sourcetxid.GetHex() << std::endl); return -1; } if (sourcetx.vout.size() == 0) { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "no vouts in source txid=" << sourcetxid.GetHex() << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "no vouts in source txid=" << sourcetxid.GetHex() << std::endl); return -1; } // might be malleable: if (burnTx.nExpiryHeight != sourcetx.nExpiryHeight) { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "burntx nExpiryHeight incorrect for source txid=" << sourcetxid.GetHex() << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "burntx nExpiryHeight incorrect for source txid=" << sourcetxid.GetHex() << std::endl); return -1; } @@ -593,7 +510,7 @@ int32_t CheckPUBKEYimport(TxProof proof,std::vector rawproof,CTransacti vopret.size() == 0 || !E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> amount) || evalCode != EVAL_IMPORTCOIN || funcId != 'A') { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "none or incorrect opret to validate in source txid=" << sourcetxid.GetHex() << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "none or incorrect opret to validate in source txid=" << sourcetxid.GetHex() << std::endl); return -1; } @@ -601,7 +518,7 @@ int32_t CheckPUBKEYimport(TxProof proof,std::vector rawproof,CTransacti // amount malleability check with the opret from the source tx: if (payouts[0].nValue != amount) { // assume that burntx amount is checked in the common code in Eval::ImportCoin() - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "importTx amount != amount in the opret of source txid=" << sourcetxid.GetHex() << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "importTx amount != amount in the opret of source txid=" << sourcetxid.GetHex() << std::endl); return -1; } @@ -621,17 +538,18 @@ bool CheckMigration(Eval *eval, const CTransaction &importTx, const CTransaction if ( is_STAKED(ASSETCHAINS_SYMBOL) == 1 ) return eval->Invalid("no-tokens-migrate-on-LABS"); struct CCcontract_info *cpTokens, CCtokens_info; - std::vector oprets; + std::vector> oprets; + uint8_t evalCodeInOpret; std::vector voutTokenPubkeys; vscript_t vnonfungibleOpret; cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS); - if (DecodeTokenOpRetV1(importTx.vout.back().scriptPubKey, tokenid, voutTokenPubkeys, oprets) == 0) + if (DecodeTokenOpRet(importTx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) == 0) return eval->Invalid("cannot-decode-import-tx-token-opret"); uint8_t nonfungibleEvalCode = EVAL_TOKENS; // init to no non-fungibles - GetOpReturnCCBlob(oprets, vnonfungibleOpret); + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vnonfungibleOpret); if (!vnonfungibleOpret.empty()) nonfungibleEvalCode = vnonfungibleOpret.begin()[0]; @@ -674,24 +592,25 @@ bool CheckMigration(Eval *eval, const CTransaction &importTx, const CTransaction return eval->Invalid("cannot-unmarshal-rawproof-for-tokens"); uint256 sourceTokenId; - std::vector oprets; + std::vector> oprets; + uint8_t evalCodeInOpret; std::vector voutTokenPubkeys; - if (burnTx.vout.size() > 0 && DecodeTokenOpRetV1(burnTx.vout.back().scriptPubKey, sourceTokenId, voutTokenPubkeys, oprets) == 0) + if (burnTx.vout.size() > 0 && DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCodeInOpret, sourceTokenId, voutTokenPubkeys, oprets) == 0) return eval->Invalid("cannot-decode-burn-tx-token-opret"); if (sourceTokenId != tokenbaseTx.GetHash()) // check tokenid in burn tx opret maches the passed tokenbase tx (to prevent cheating by importing user) return eval->Invalid("incorrect-token-creation-tx-passed"); - std::vector opretsSrc; + std::vector> opretsSrc; vscript_t vorigpubkeySrc; std::string nameSrc, descSrc; - if (DecodeTokenCreateOpRetV1(tokenbaseTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsSrc) == 0) + if (DecodeTokenCreateOpRet(tokenbaseTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsSrc) == 0) return eval->Invalid("cannot-decode-token-creation-tx"); - std::vector opretsImport; + std::vector> opretsImport; vscript_t vorigpubkeyImport; std::string nameImport, descImport; - if (importTx.vout.size() == 0 || DecodeTokenCreateOpRetV1(importTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsImport) == 0) + if (importTx.vout.size() == 0 || DecodeTokenCreateOpRet(importTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsImport) == 0) return eval->Invalid("cannot-decode-token-import-tx"); // check that name,pubkey,description in import tx correspond ones in token creation tx in the source chain: @@ -715,7 +634,7 @@ bool CheckMigration(Eval *eval, const CTransaction &importTx, const CTransaction uint256 target = merkleBranchProof.second.Exec(burnTx.GetHash()); LOGSTREAM("importcoin", CCLOG_DEBUG2, stream << "Eval::ImportCoin() momom target=" << target.GetHex() << " merkleBranchProof.first=" << merkleBranchProof.first.GetHex() << std::endl); if (!CheckMoMoM(merkleBranchProof.first, target)) { - LOGSTREAM("importcoin", CCLOG_ERROR, stream << "MoMoM check failed for importtx=" << importTx.GetHash().GetHex() << std::endl); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "MoMoM check failed for importtx=" << importTx.GetHash().GetHex() << std::endl); return eval->Invalid("momom-check-fail"); } } @@ -745,10 +664,10 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp std::vector publishers; uint32_t targetCcid; std::string targetSymbol, srcaddr, destaddr, receipt, rawburntx; - uint256 payoutsHash, bindtxid, burntxid, pegstxid, tokenid; + uint256 payoutsHash, bindtxid, burntxid; std::vector rawproof; std::vector txids; - CPubKey destpub,srcpub,accountpk; std::pair account(0,0); + CPubKey destpub; LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "Validating import tx..., txid=" << importTx.GetHash().GetHex() << std::endl); @@ -821,8 +740,8 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp { if ( ASSETCHAINS_SELFIMPORT != "PEGSCC" ) return Invalid("PEGSCC-import-when-not PEGSCC"); - else if ( UnmarshalBurnTx(burnTx,pegstxid,tokenid,srcpub,amount,account,accountpk)==0 || CheckPegsimport(importTx,pegstxid,tokenid,srcpub,amount,account,accountpk) < 0 ) - return Invalid("PEGSCC-import-failure"); + // else if ( CheckPUBKEYimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 ) + // return Invalid("PEGSCC-import-failure"); } else if ( targetSymbol == "PUBKEY" ) { diff --git a/src/cc/importgateway.cpp b/src/cc/importgateway.cpp index a71d22f49e2..16b54d2b634 100644 --- a/src/cc/importgateway.cpp +++ b/src/cc/importgateway.cpp @@ -23,8 +23,7 @@ #define KMD_P2SHTYPE 85 #define KMD_WIFTYPE 188 #define KMD_TADDR 0 -#define CC_MARKER_VALUE 1000 -#define CC_TXFEE 10000 +#define CC_MARKER_VALUE 10000 extern uint256 KOMODO_EARLYTXID; @@ -35,26 +34,27 @@ CScript EncodeImportGatewayBindOpRet(uint8_t funcid,std::string coin,uint256 ora return(opret); } -uint8_t DecodeImportGatewayBindOpRet(char *depositaddr,const CScript &scriptPubKey,std::string &coin,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &importgatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype) +uint8_t DecodeImportGatewayBindOpRet(char *burnaddr,const CScript &scriptPubKey,std::string &coin,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &importgatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype) { std::vector vopret; uint8_t *script,e,f; std::vector pubkeys; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - depositaddr[0] = 0; - if ( vopret.size() > 2 && script[0]==EVAL_IMPORTGATEWAY && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> oracletxid; ss >> M; ss >> N; ss >> importgatewaypubkeys; ss >> taddr; ss >> prefix; ss >> prefix2; ss >> wiftype) != 0 ) + burnaddr[0] = 0; + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> oracletxid; ss >> M; ss >> N; ss >> importgatewaypubkeys; ss >> taddr; ss >> prefix; ss >> prefix2; ss >> wiftype) != 0 ) { if ( prefix == KMD_PUBTYPE && prefix2 == KMD_P2SHTYPE ) { if ( N > 1 ) { - strcpy(depositaddr,CBitcoinAddress(CScriptID(GetScriptForMultisig(M,importgatewaypubkeys))).ToString().c_str()); - } else Getscriptaddress(depositaddr,CScript() << ParseHex(HexStr(importgatewaypubkeys[0])) << OP_CHECKSIG); + strcpy(burnaddr,CBitcoinAddress(CScriptID(GetScriptForMultisig(M,importgatewaypubkeys))).ToString().c_str()); + LOGSTREAM("importgateway", CCLOG_DEBUG1, stream << "f." << f << " M." << (int)M << " of N." << (int)N << " size." << (int32_t)importgatewaypubkeys.size() << " -> " << burnaddr << std::endl); + } else Getscriptaddress(burnaddr,CScript() << ParseHex(HexStr(importgatewaypubkeys[0])) << OP_CHECKSIG); } else { - if ( N > 1 ) strcpy(depositaddr,CCustomBitcoinAddress(CScriptID(GetScriptForMultisig(M,importgatewaypubkeys)),taddr,prefix,prefix2).ToString().c_str()); - else GetCustomscriptaddress(depositaddr,CScript() << ParseHex(HexStr(importgatewaypubkeys[0])) << OP_CHECKSIG,taddr,prefix,prefix2); + if ( N > 1 ) strcpy(burnaddr,CCustomBitcoinAddress(CScriptID(GetScriptForMultisig(M,importgatewaypubkeys)),taddr,prefix,prefix2).ToString().c_str()); + else GetCustomscriptaddress(burnaddr,CScript() << ParseHex(HexStr(importgatewaypubkeys[0])) << OP_CHECKSIG,taddr,prefix,prefix2); } return(f); } else LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "error decoding bind opret" << std::endl); @@ -75,69 +75,89 @@ uint8_t DecodeImportGatewayDepositOpRet(const CScript &scriptPubKey,uint256 &bin GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0]==EVAL_IMPORTGATEWAY && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> refcoin; ss >> bindtxid; ss >> publishers; ss >> txids; ss >> height; ss >> burntxid; ss >> claimvout; ss >> deposithex; ss >> proof; ss >> destpub; ss >> amount) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> refcoin; ss >> bindtxid; ss >> publishers; ss >> txids; ss >> height; ss >> burntxid; ss >> claimvout; ss >> deposithex; ss >> proof; ss >> destpub; ss >> amount) != 0 ) { return(f); } return(0); } -CScript EncodeImportGatewayWithdrawOpRet(uint8_t funcid,uint256 bindtxid,CPubKey mypk,std::string refcoin,CPubKey withdrawpub,int64_t amount) +CScript EncodeImportGatewayWithdrawOpRet(uint8_t funcid,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) { CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << bindtxid << mypk << refcoin << withdrawpub << amount); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << bindtxid << refcoin << withdrawpub << amount); return(opret); } -uint8_t DecodeImportGatewayWithdrawOpRet(const CScript &scriptPubKey,uint256 &bindtxid,CPubKey &mypk,std::string &refcoin,CPubKey &withdrawpub,int64_t &amount) +uint8_t DecodeImportGatewayWithdrawOpRet(const CScript &scriptPubKey,uint256 &bindtxid,std::string &refcoin,CPubKey &withdrawpub,int64_t &amount) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0]==EVAL_IMPORTGATEWAY && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bindtxid; ss >> mypk; ss >> refcoin; ss >> withdrawpub; ss >> amount) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bindtxid; ss >> refcoin; ss >> withdrawpub; ss >> amount) != 0 ) { return(f); } return(0); } -CScript EncodeImportGatewayWithdrawSignOpRet(uint8_t funcid,uint256 withdrawtxid, uint256 lasttxid,std::vector signingpubkeys,std::string refcoin,uint8_t K,std::string hex) +CScript EncodeImportGatewayPartialOpRet(uint8_t funcid, uint256 withdrawtxid,std::string refcoin,uint8_t K, CPubKey signerpk,std::string hex) { CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << lasttxid << signingpubkeys << refcoin << K << hex); + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << signerpk << hex); + return(opret); +} + +uint8_t DecodeImportGatewayPartialOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,CPubKey &signerpk,std::string &hex) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> K; ss >> signerpk; ss >> hex) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodeImportGatewayCompleteSigningOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint8_t K,std::string hex) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << hex); return(opret); } -uint8_t DecodeImportGatewayWithdrawSignOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid, uint256 &lasttxid,std::vector &signingpubkeys,std::string &refcoin,uint8_t &K,std::string &hex) +uint8_t DecodeImportGatewayCompleteSigningOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,std::string &hex) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0]==EVAL_IMPORTGATEWAY && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> lasttxid; ss >> signingpubkeys; ss >> refcoin; ss >> K; ss >> hex) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> K; ss >> hex) != 0 ) { return(f); } return(0); } -CScript EncodeImportGatewayMarkDoneOpRet(uint8_t funcid,uint256 withdrawtxid,CPubKey mypk,std::string refcoin,uint256 withdrawsigntxid) +CScript EncodeImportGatewayMarkDoneOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint256 completetxid) { CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << mypk << refcoin << withdrawsigntxid); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << completetxid); return(opret); } -uint8_t DecodeImportGatewayMarkDoneOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,CPubKey &mypk,std::string &refcoin, uint256 &withdrawsigntxid) +uint8_t DecodeImportGatewayMarkDoneOpRet(const CScript &scriptPubKey, uint256 &withdrawtxid, std::string &refcoin, uint256 &completetxid) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0]==EVAL_IMPORTGATEWAY && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> mypk; ss >> refcoin; ss >> withdrawsigntxid;) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> completetxid;) != 0 ) { return(f); } @@ -153,7 +173,7 @@ uint8_t DecodeImportGatewayOpRet(const CScript &scriptPubKey) if ( vopret.size() > 2 && script[0] == EVAL_IMPORTGATEWAY) { f=script[1]; - if (f == 'B' || f == 'D' || f == 'C' || f == 'W' || f == 'T' || f == 'S' || f == 'M') + if (f == 'B' || f == 'D' || f == 'C' || f == 'W' || f == 'P' || f == 'S' || f == 'M') return(f); } return(0); @@ -171,30 +191,30 @@ int64_t IsImportGatewayvout(struct CCcontract_info *cp,const CTransaction& tx,in return(0); } -int64_t ImportGatewayVerify(char *refdepositaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 burntxid,const std::string deposithex,std::vectorproof,uint256 merkleroot,CPubKey destpub,uint8_t taddr,uint8_t prefix,uint8_t prefix2) +int64_t ImportGatewayVerify(char *refburnaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 burntxid,const std::string deposithex,std::vectorproof,uint256 merkleroot,CPubKey destpub,uint8_t taddr,uint8_t prefix,uint8_t prefix2) { std::vector txids; uint256 proofroot,hashBlock,txid = zeroid; CTransaction tx; std::string name,description,format; char destaddr[64],destpubaddr[64],claimaddr[64]; int32_t i,numvouts; int64_t nValue = 0; if ( myGetTransaction(oracletxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) { - LOGSTREAM("importgateway",CCLOG_ERROR, stream << "ImportGatewayVerify cant find oracletxid " << oracletxid.GetHex() << std::endl); + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify cant find oracletxid " << oracletxid.GetHex() << std::endl); return(0); } if ( DecodeOraclesCreateOpRet(tx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' || name != refcoin ) { - LOGSTREAM("importgateway",CCLOG_ERROR, stream << "ImportGatewayVerify mismatched oracle name " << name << " != " << refcoin << std::endl); + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify mismatched oracle name " << name << " != " << refcoin << std::endl); return(0); } proofroot = BitcoinGetProofMerkleRoot(proof,txids); if ( proofroot != merkleroot ) { - LOGSTREAM("importgateway",CCLOG_ERROR, stream << "ImportGatewayVerify mismatched merkleroot " << proofroot.GetHex() << " != " << merkleroot.GetHex() << std::endl); + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify mismatched merkleroot " << proofroot.GetHex() << " != " << merkleroot.GetHex() << std::endl); return(0); } if (std::find(txids.begin(), txids.end(), burntxid) == txids.end()) { - LOGSTREAM("importgateway",CCLOG_ERROR, stream << "ImportGatewayVerify invalid proof for this burntxid " << burntxid.GetHex() << std::endl); + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify invalid proof for this burntxid " << burntxid.GetHex() << std::endl); return 0; } if ( DecodeHexTx(tx,deposithex) != 0 ) @@ -206,7 +226,7 @@ int64_t ImportGatewayVerify(char *refdepositaddr,uint256 oracletxid,int32_t clai for (i=0; i pubkeys; CTransaction tx; std::vector txids; _GetCCaddress(markeraddr,EVAL_IMPORTGATEWAY,importgatewaypk); - SetCCtxids(txids,markeraddr,true,cp->evalcode,CC_MARKER_VALUE,zeroid,'B'); + SetCCtxids(txids,markeraddr,true,cp->evalcode,zeroid,'B'); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { if ( myGetTransaction(*it,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey)=='B' ) { - if ( DecodeImportGatewayBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B' ) + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B' ) { if ( coin == refcoin ) { - LOGSTREAM("importgateway",CCLOG_ERROR, stream << "trying to bind an existing import for coin" << std::endl); + LOGSTREAM("importgateway",CCLOG_INFO, stream << "trying to bind an existing import for coin" << std::endl); return(1); } } @@ -264,8 +284,8 @@ int32_t ImportGatewayBindExists(struct CCcontract_info *cp,CPubKey importgateway { const CTransaction &txmempool = *it; - if ((numvouts=txmempool.vout.size()) > 0 && txmempool.vout[0].nValue==CC_MARKER_VALUE && DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey)=='B') - if (DecodeImportGatewayBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B') + if ((numvouts=txmempool.vout.size()) > 0 && DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey)=='B') + if (DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B') return(1); } @@ -274,12 +294,13 @@ int32_t ImportGatewayBindExists(struct CCcontract_info *cp,CPubKey importgateway bool ImportGatewayValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn) { - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval; uint8_t funcid,K,tmpK,M,N,taddr,prefix,prefix2,wiftype; - char str[65],depositaddr[65],importgatewayaddr[65]; - std::vector pubkeys,signingpubkeys,tmpsigningpubkeys; int64_t amount; - uint256 hashblock,bindtxid,deposittxid,withdrawtxid,tmpwithdrawtxid,lasttxid,tmplasttxid,withdrawsigntxid,oracletxid; CTransaction tmptx; - std::string refcoin,tmprefcoin,hex,tmphex; CPubKey pk,tmppk,destpub,importgatewaypk; + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks,height,claimvout; bool retval; uint8_t funcid,hash[32],K,M,N,taddr,prefix,prefix2,wiftype; + char str[65],destaddr[65],burnaddr[65],importgatewayaddr[65],validationError[512]; + std::vector txids; std::vector pubkeys,publishers,tmppublishers; std::vector proof; int64_t amount,tmpamount; + uint256 hashblock,txid,bindtxid,deposittxid,withdrawtxid,completetxid,tmptokenid,oracletxid,bindtokenid,burntxid,tmptxid,merkleroot,mhash; CTransaction bindtx,tmptx; + std::string refcoin,tmprefcoin,hex,name,description,format; CPubKey pubkey,tmppubkey,importgatewaypk; + return (true); numvins = tx.vin.size(); numvouts = tx.vout.size(); preventCCvins = preventCCvouts = -1; @@ -304,121 +325,141 @@ bool ImportGatewayValidate(struct CCcontract_info *cp,Eval *eval,const CTransact //vin.0: normal input //vout.0: CC vout marker //vout.n-1: opreturn - 'B' coin oracletxid M N pubkeys taddr prefix prefix2 wiftype - return eval->Invalid("unexpected ImportGatewayValidate for importgatewaybind!"); + return eval->Invalid("unexpected ImportGatewayValidate for gatewaysbind!"); break; case 'W': - //vin.0: normal input - //vout.0: CC vout marker to importgateway CC address - //vout.1: normal vout to CCtxidaddr of importgatewaybind txid (burn coins) - //vout.2: normal change back to owners pubkey (if any) - //vout.n-1: opreturn - 'W' bindtxid mypk refcoin withdrawpub amount - return eval->Invalid("unexpected ImportGatewayValidate for importgatewaywithdraw!"); + //vin.0: normal input + //vin.1: CC input of tokens + //vout.0: CC vout marker to gateways CC address + //vout.1: CC vout of gateways tokens back to gateways tokens CC address + //vout.2: CC vout change of tokens back to owners pubkey (if any) + //vout.n-1: opreturn - 'W' bindtxid refcoin withdrawpub amount + return eval->Invalid("unexpected ImportGatewayValidate for gatewaysWithdraw!"); + break; + case 'P': + //vin.0: normal input + //vin.1: CC input of marker from previous tx (withdraw or partialsing) + //vout.0: CC vout marker to gateways CC address + //vout.n-1: opreturn - 'P' withdrawtxid refcoin number_of_signs mypk hex + if ((numvouts=tx.vout.size()) > 0 && DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,pubkey,hex)!='P') + return eval->Invalid("invalid gatewaysPartialSign OP_RETURN data!"); + else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); + else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[1],1,importgatewayaddr,amount)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); + else if (tmptx.vout[1].nValue!=amount) + return eval->Invalid("amount in opret not matching tx tokens amount!"); + else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,tmprefcoin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (komodo_txnotarizedconfirmed(bindtxid) == false) + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewayspartialsign!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewayspartialsign or invalid marker amount!"); + else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) + return eval->Invalid("vout.0 invalid marker for gatewayspartialsign!"); + else if (K>M) + return eval->Invalid("invalid number of signs!"); break; case 'S': //vin.0: normal input - //vin.1: CC input of marker from previous tx (withdraw or withdrawsign) - //vout.0: CC vout marker to importgateway CC address - //vout.n-1: opreturn - 'S' withdrawtxid lasttxid mypk refcoin number_of_signs hex - if ((numvouts=tx.vout.size()) > 0 && DecodeImportGatewayWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,lasttxid,signingpubkeys,refcoin,K,hex)!='S') - return eval->Invalid("invalid importgatewaywithdrawsign OP_RETURN data!"); - else if (lasttxid!=withdrawtxid && myGetTransaction(lasttxid,tmptx,hashblock) == 0) - return eval->Invalid("invalid last txid!"); - else if (lasttxid!=withdrawtxid && (numvouts=tx.vout.size()) > 0 && DecodeImportGatewayWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,tmpwithdrawtxid,tmplasttxid,tmpsigningpubkeys,tmprefcoin,tmpK,tmphex)!='S') - return eval->Invalid("invalid last importgatewaywithdrawsign OP_RETURN data!"); - else if (lasttxid!=withdrawtxid && CompareHexVouts(hex,tmphex)==0) - return eval->Invalid("invalid importgatewaywithdrawsign, modifying initial tx vouts in hex!"); + //vin.1: CC input of marker from previous tx (withdraw or partialsing) + //vout.0: CC vout marker to gateways CC address + //vout.n-1: opreturn - 'S' withdrawtxid refcoin hex + if ((numvouts=tx.vout.size()) > 0 && DecodeImportGatewayCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,hex)!='S') + return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!"); else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) - return eval->Invalid("invalid importgatewaywithdraw txid!"); - else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,tmppk,tmprefcoin,destpub,amount)!='W') - return eval->Invalid("invalid importgatewaywithdraw OP_RETURN data!"); + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); else if (tmprefcoin!=refcoin) - return eval->Invalid("refcoin different than in withdraw tx"); + return eval->Invalid("refcoin different than in bind tx"); else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for importgatewaywithdraw!"); + return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); + else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for gatewaysWithdraw!"); else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) - return eval->Invalid("invalid marker vout for importgatewaywithdraw!"); - else if ( CCtxidaddr(str,bindtxid)==CPubKey() || ConstrainVout(tmptx.vout[1],0,str,amount)==0) - return eval->Invalid("invalid destination of coins or amount for burn in importgatewaywithdraw!"); + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[1],1,importgatewayaddr,amount)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); + else if (tmptx.vout[1].nValue!=amount) + return eval->Invalid("amount in opret not matching tx tokens amount!"); else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) - return eval->Invalid("importgatewaywithdraw tx is not yet confirmed(notarised)!"); - else if (KOMODO_EARLYTXID!=zeroid && bindtxid!=KOMODO_EARLYTXID) - return eval->Invalid("invalid importgatewaybind txid. On this chain only valid import gateway is " + KOMODO_EARLYTXID.GetHex()); + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) - return eval->Invalid("invalid importgatewaybind txid!"); - else if ((numvouts=tmptx.vout.size()) < 1 || DecodeImportGatewayBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tmprefcoin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') - return eval->Invalid("invalid importgatewaybind OP_RETURN data!"); + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,tmprefcoin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); else if (tmprefcoin!=refcoin) return eval->Invalid("refcoin different than in bind tx"); - else if ( N == 0 || N > 15 || M > N ) - return eval->Invalid("invalid N or M for importgatewaybind"); - else if (pubkeys.size()!=N) - return eval->Invalid("not enough pubkeys supplied in importgatewaybind for given N"); else if (komodo_txnotarizedconfirmed(bindtxid) == false) - return eval->Invalid("importgatewaybind tx is not yet confirmed(notarised)!"); + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); else if (IsCCInput(tx.vin[0].scriptSig) != 0) - return eval->Invalid("vin.0 is normal for importgatewaywithdrawsign!"); - else if (CheckVinPk(tx,0,pubkeys)==0) - return eval->Invalid("vin.0 invalid, importgatewaywithdrawsign must be created by one of the importgateway pubkeys owner!"); + return eval->Invalid("vin.0 is normal for gatewayscompletesigning!"); else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) - return eval->Invalid("vin.1 is CC marker for importgatewaywithdrawsign or invalid marker amount!"); + return eval->Invalid("vin.1 is CC marker for gatewayscompletesigning or invalid marker amount!"); else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) - return eval->Invalid("vout.0 invalid marker for importgatewaywithdrawsign!"); - else if (std::find(pubkeys.begin(),pubkeys.end(),signingpubkeys[signingpubkeys.size()-1])==pubkeys.end()) - return eval->Invalid("invalid pubkey in importgatewaywithdrawsign OP_RETURN, must be one of importgateway pubkeys!"); + return eval->Invalid("vout.0 invalid marker for gatewayscompletesigning!"); + else if (KInvalid("invalid number of signs!"); break; case 'M': //vin.0: normal input - //vin.1: CC input of importgatewaywithdrawsign tx marker to importgateway CC address - //vout.0; marker value back to users pubkey - //vout.n-1: opreturn - 'M' withdrawtxid mypk refcoin withdrawsigntxid - if ((numvouts=tx.vout.size()) > 0 && DecodeImportGatewayMarkDoneOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,pk,refcoin,withdrawsigntxid)!='M') - return eval->Invalid("invalid importgatewaymarkdone OP_RETURN data!"); - else if (myGetTransaction(withdrawsigntxid,tmptx,hashblock) == 0) - return eval->Invalid("invalid importgatewaywithdrawsign txid!"); - else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawSignOpRet(tmptx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,tmprefcoin,K,hex)!='S') - return eval->Invalid("invalid importgatewaywithdrawsign OP_RETURN data!"); + //vin.1: CC input of gatewayscompletesigning tx marker to gateways CC address + //vout.0: opreturn - 'M' withdrawtxid refcoin completetxid + if ((numvouts=tx.vout.size()) > 0 && DecodeImportGatewayMarkDoneOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,completetxid)!='M') + return eval->Invalid("invalid gatewaysmarkdone OP_RETURN data!"); + else if (myGetTransaction(completetxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewayscompletesigning txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayCompleteSigningOpRet(tmptx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmprefcoin,K,hex)!='S') + return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!"); + else if (komodo_txnotarizedconfirmed(completetxid) == false) + return eval->Invalid("gatewayscompletesigning tx is not yet confirmed(notarised)!"); else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) - return eval->Invalid("invalid importgatewaywithdraw txid!"); - else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,tmppk,tmprefcoin,destpub,amount)!='W') - return eval->Invalid("invalid importgatewaywithdraw OP_RETURN data!"); + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); else if (tmprefcoin!=refcoin) - return eval->Invalid("refcoin different than in withdraw tx"); - else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for importgatewaywithdraw!"); - else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) - return eval->Invalid("invalid marker vout for importgatewaywithdraw!"); - else if ( CCtxidaddr(str,bindtxid)==CPubKey() || ConstrainVout(tmptx.vout[1],0,str,amount)==0) - return eval->Invalid("invalid destination of coins or amount for burn in importgatewaywithdraw!"); + return eval->Invalid("refcoin different than in bind tx"); else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) - return eval->Invalid("importgatewaywithdraw tx is not yet confirmed(notarised)!"); - else if (KOMODO_EARLYTXID!=zeroid && bindtxid!=KOMODO_EARLYTXID) - return eval->Invalid("invalid importgatewaybind txid. On this chain only valid import gateway is " + KOMODO_EARLYTXID.GetHex()); + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) - return eval->Invalid("invalid importgatewaybind txid!"); - else if ((numvouts=tmptx.vout.size()) < 1 || DecodeImportGatewayBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tmprefcoin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') - return eval->Invalid("invalid importgatewaybind OP_RETURN data!"); + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,tmprefcoin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); else if (tmprefcoin!=refcoin) return eval->Invalid("refcoin different than in bind tx"); - else if ( N == 0 || N > 15 || M > N ) - return eval->Invalid("invalid N or M for importgatewaybind"); - else if (pubkeys.size()!=N) - return eval->Invalid("not enough pubkeys supplied in importgatewaybind for given N"); else if (komodo_txnotarizedconfirmed(bindtxid) == false) - return eval->Invalid("importgatewaybind tx is not yet confirmed(notarised)!"); + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for importgatewaymarkdone!"); - else if (CheckVinPk(tx,0,pubkeys)==0) - return eval->Invalid("vin.0 invalid, importgatewaymarkdone must be created by one of the importgateway pubkeys owner!"); + return eval->Invalid("vin.0 is normal for gatewaysmarkdone!"); else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) - return eval->Invalid("vin.1 is CC marker for importgatewaymarkdone or invalid marker amount!"); - else if (std::find(pubkeys.begin(),pubkeys.end(),signingpubkeys[signingpubkeys.size()-1])==pubkeys.end()) - return eval->Invalid("invalid pubkey in importgatewaymarkdone OP_RETURN, must be one of importgateway pubkeys!"); + return eval->Invalid("vin.1 is CC marker for gatewaysmarkdone or invalid marker amount!"); + else if (KInvalid("invalid number of signs!"); break; } } retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); if ( retval != 0 ) - LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "ImportGateway tx validated" << std::endl); + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGateway tx validated" << std::endl); else fprintf(stderr,"ImportGateway tx invalid\n"); return(retval); // } @@ -428,14 +469,15 @@ bool ImportGatewayValidate(struct CCcontract_info *cp,Eval *eval,const CTransact // helper functions for rpc calls in rpcwallet.cpp -UniValue ImportGatewayBind(const CPubKey& pk, uint64_t txfee,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4) +std::string ImportGatewayBind(uint64_t txfee,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction oracletx,tx; uint8_t taddr,prefix,prefix2,wiftype; CPubKey mypk,regpk,importgatewaypk; CScript opret; uint256 hashBlock,txid,tmporacletxid; - struct CCcontract_info *cp,C; std::string name,description,format; int32_t i,n=0,numvouts; int64_t datafee; - char destaddr[64],coinaddr[64],markeraddr[64],*fstr; std::vector tmppubkeys; std::vector > unspentOutputs; + CTransaction oracletx; uint8_t taddr,prefix,prefix2,wiftype; CPubKey mypk,importgatewaypk; CScript opret; uint256 hashBlock; + struct CCcontract_info *cp,*cpTokens,C,CTokens; std::string name,description,format; int32_t i,numvouts; + char destaddr[64],coinaddr[64],myTokenCCaddr[64],str[65],*fstr; cp = CCinit(&C,EVAL_IMPORTGATEWAY); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); if (coin=="KMD") { prefix = KMD_PUBTYPE; @@ -452,82 +494,112 @@ UniValue ImportGatewayBind(const CPubKey& pk, uint64_t txfee,std::string coin,ui LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "set prefix " << prefix << ", prefix2 " << prefix2 << ", wiftype " << wiftype << ", taddr " << taddr << " for " << coin << std::endl); } if ( N == 0 || N > 15 || M > N ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "illegal M." << M << " or N." << N); + { + CCerror = strprintf("illegal M.%d or N.%d",M,N); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if ( pubkeys.size() != N ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "M."< >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - txid = it->first.txhash; - if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 - && DecodeOraclesOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid) - { - std::vector::iterator it1 = std::find(pubkeys.begin(), pubkeys.end(), regpk); - if (it1 != pubkeys.end()) - n++; - } + CCerror = strprintf("M.%d N.%d but pubkeys[%d]",M,N,(int32_t)pubkeys.size()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); } - if (pubkeys.size()!=n) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "different number of bind and oracle pubkeys " << n << "!=" << pubkeys.size() << std::endl); for (i=0; i 0 ) + { + CCerror = strprintf("Gateway bind.%s already exists",coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,2) > 0 ) { mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,importgatewaypk)); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeImportGatewayBindOpRet('B',coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype))); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeImportGatewayBindOpRet('B',coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype))); } - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cant find enough inputs"); + CCerror = strprintf("cant find enough inputs"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); } -UniValue ImportGatewayDeposit(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t claimvout,std::string rawburntx,std::vectorproof,CPubKey destpub,uint64_t amount) +std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t claimvout,std::string rawburntx,std::vectorproof,CPubKey destpub,int64_t amount) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()), burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction bindtx; CPubKey mypk; uint256 oracletxid,merkleroot,mhash,hashBlock,txid; std::vector vouts; int32_t i,m,n,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::string coin; struct CCcontract_info *cp,C; - std::vector pubkeys,publishers; std::vector txids; char str[65],depositaddr[64]; + std::vector pubkeys,publishers; std::vector txids; char str[128],burnaddr[64]; if (KOMODO_EARLYTXID!=zeroid && bindtxid!=KOMODO_EARLYTXID) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid import gateway. On this chain only valid import gateway is " << KOMODO_EARLYTXID.GetHex()); + { + CCerror = strprintf("invalid import gateway. On this chain only valid import gateway is %s",KOMODO_EARLYTXID.GetHex()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } cp = CCinit(&C,EVAL_IMPORTGATEWAY); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_IMPORTGATEWAY]?0:CC_TXFEE; - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); if (!E_UNMARSHAL(ParseHex(rawburntx), ss >> burntx)) { return std::string(""); } + LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "ImportGatewayDeposit ht." << height << " " << refcoin << " " << (double)amount/COIN << " numpks." << (int32_t)pubkeys.size() << std::endl); if ( myGetTransaction(bindtxid,bindtx,hashBlock) == 0 || (numvouts= bindtx.vout.size()) <= 0 ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cant find bindtxid " << bindtxid.GetHex()); - if ( DecodeImportGatewayBindOpRet(depositaddr,bindtx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid bindtxid " << bindtxid.GetHex()); - if (refcoin != coin ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid coin - bindtxid " << bindtxid.GetHex() << "coin." << coin); + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,bindtx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) + { + CCerror = strprintf("invalid coin - bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if (komodo_txnotarizedconfirmed(bindtxid)==false) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "gatewaysbind tx not yet confirmed/notarized"); - if (!destpub.IsFullyValid()) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "destination pubkey is invalid"); - LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "ImportGatewayDeposit ht." << height << " " << refcoin << " " << (double)amount/COIN << " numpks." << (int32_t)pubkeys.size() << std::endl); + { + CCerror = strprintf("gatewaysbind tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } n = (int32_t)pubkeys.size(); merkleroot = zeroid; for (i=m=0; i leaftxids; BitcoinGetProofMerkleRoot(proof, leaftxids); MerkleBranch newBranch(0, leaftxids); - TxProof txProof = std::make_pair(burntxid, newBranch); - return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(ImportProof(txProof), burntx, vouts))); + TxProof txProof = std::make_pair(burntxid, newBranch); + return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof, burntx, vouts))); } -UniValue ImportGatewayWithdraw(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) +std::string ImportGatewayWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction tx; CPubKey mypk,tmppk,importgatewaypk,tmpwithdrawpub; uint256 txid,hashBlock,oracletxid,tmpbindtxid,withdrawtxid; int32_t i,n,vout,numvouts; - int64_t nValue,inputs,CCchange=0,balance; uint8_t funcid,K,M,N,taddr,prefix,prefix2,wiftype; std::string coin,hex; - std::vector msigpubkeys; char str[65],depositaddr[64],coinaddr[64]; struct CCcontract_info *cp,C; + CTransaction tx; CPubKey mypk,importgatewaypk,signerpk; uint256 txid,hashBlock,oracletxid,tmptokenid,tmpbindtxid,withdrawtxid; int32_t vout,numvouts; + int64_t nValue,inputs,CCchange=0,tmpamount; uint8_t funcid,K,M,N,taddr,prefix,prefix2,wiftype; std::string coin,hex; + std::vector msigpubkeys; char burnaddr[64],str[65],coinaddr[64]; struct CCcontract_info *cp,C; std::vector > unspentOutputs; if (KOMODO_EARLYTXID!=zeroid && bindtxid!=KOMODO_EARLYTXID) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid import gateway. On this chain only valid import gateway is " << KOMODO_EARLYTXID.GetHex()); + { + CCerror = strprintf("invalid import gateway. On this chain only valid import gateway is %s",KOMODO_EARLYTXID.GetHex()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } cp = CCinit(&C,EVAL_IMPORTGATEWAY); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_IMPORTGATEWAY]?0:CC_TXFEE; - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); importgatewaypk = GetUnspendable(cp, 0); + if( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cant find bindtxid " << bindtxid.GetHex()); - if( DecodeImportGatewayBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || coin != refcoin ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid bindtxid " << bindtxid.GetHex() << " coin." << coin); + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || coin != refcoin ) + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if (komodo_txnotarizedconfirmed(bindtxid)==false) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "importgatewaybind tx not yet confirmed/notarized"); - if (!withdrawpub.IsFullyValid()) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "withdraw destination pubkey is invalid"); - n = (int32_t)msigpubkeys.size(); - for (i=0; i balance ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "withdraw amount is not possible, deposit balance is lower than the amount!"); - if( AddNormalinputs(mtx, mypk, txfee+CC_MARKER_VALUE+amount, 64,pk.IsValid()) > 0 ) + { + CCerror = strprintf("gatewaysbind tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + _GetCCaddress(coinaddr,EVAL_IMPORTGATEWAY,importgatewaypk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + K=0; + if ( vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size())>0 && + (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='P')) + { + if (funcid=='W' && DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,tmpamount)=='W' + && refcoin==coin && tmpbindtxid==bindtxid) + { + CCerror = strprintf("unable to create withdraw, another withdraw pending"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + + else if (funcid=='P' && DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,hex)=='P' && + myGetTransaction(withdrawtxid,tx,hashBlock)!=0 && (numvouts=tx.vout.size())>0 && DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,tmpamount)=='W' + && refcoin==coin && tmpbindtxid==bindtxid) + { + CCerror = strprintf("unable to create withdraw, another withdraw pending"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + } + if( AddNormalinputs(mtx, mypk, txfee+CC_MARKER_VALUE+amount, 64) > 0 ) { mtx.vout.push_back(MakeCC1vout(EVAL_IMPORTGATEWAY,CC_MARKER_VALUE,importgatewaypk)); - mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(CCtxidaddr(str,bindtxid))) << OP_CHECKSIG)); - return(FinalizeCCTxExt(pk.IsValid(),0, cp, mtx, mypk, txfee,EncodeImportGatewayWithdrawOpRet('W',bindtxid,mypk,refcoin,withdrawpub,amount))); + mtx.vout.push_back(MakeCC1vout(EVAL_IMPORTGATEWAY,amount,CCtxidaddr(str,bindtxid))); + return(FinalizeCCTx(0, cp, mtx, mypk, txfee,EncodeImportGatewayWithdrawOpRet('W',bindtxid,refcoin,withdrawpub,amount))); + } + CCerror = strprintf("cant find enough normal inputs"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string ImportGatewayPartialSign(uint64_t txfee,uint256 lasttxid,std::string refcoin, std::string hex) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,withdrawpub,signerpk,importgatewaypk; struct CCcontract_info *cp,C; CTransaction tx,tmptx; + std::vector > unspentOutputs; char funcid,str[65],burnaddr[64]; + int32_t numvouts; uint256 withdrawtxid,hashBlock,bindtxid,tokenid,oracletxid; std::string coin,tmphex; int64_t amount; + uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp,0); + if (myGetTransaction(lasttxid,tx,hashBlock)==0 || (numvouts= tx.vout.size())<=0 + || (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='P')) + { + CCerror = strprintf("can't find last tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (funcid=='W') + { + withdrawtxid=lasttxid; + if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("invalid withdraw tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (KOMODO_EARLYTXID!=zeroid && bindtxid!=KOMODO_EARLYTXID) + { + CCerror = strprintf("invalid import gateway. On this chain only valid import gateway is %s",KOMODO_EARLYTXID.GetHex()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (myGetTransaction(bindtxid,tmptx,hashBlock)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } } - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cant find enough normal inputs"); + else if (funcid=='P') + { + if (DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,tmphex)!='P' || refcoin!=coin) + { + CCerror = strprintf("cannot decode partialsign tx opret %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (myGetTransaction(withdrawtxid,tmptx,hashBlock)==0 || (numvouts= tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find withdraw tx %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' + || refcoin!=coin) + { + CCerror = strprintf("invalid withdraw tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (KOMODO_EARLYTXID!=zeroid && bindtxid!=KOMODO_EARLYTXID) + { + CCerror = strprintf("invalid import gateway. On this chain only valid import gateway is %s",KOMODO_EARLYTXID.GetHex()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (myGetTransaction(bindtxid,tmptx,hashBlock)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + if (AddNormalinputs(mtx,mypk,txfee,3)!=0) + { + mtx.vin.push_back(CTxIn(tx.GetHash(),0,CScript())); + mtx.vout.push_back(MakeCC1vout(EVAL_IMPORTGATEWAY,CC_MARKER_VALUE,importgatewaypk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeImportGatewayPartialOpRet('P',withdrawtxid,refcoin,K+1,mypk,hex))); + } + CCerror = strprintf("error adding funds for partialsign"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); } -UniValue ImportGatewayWithdrawSign(const CPubKey& pk, uint64_t txfee,uint256 lasttxid,std::string refcoin, std::string hex) +std::string ImportGatewayCompleteSigning(uint64_t txfee,uint256 lasttxid,std::string refcoin,std::string hex) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,tmppk,importgatewaypk,withdrawpub; struct CCcontract_info *cp,C; char funcid,depositaddr[64]; int64_t amount; - std::string coin,tmphex; CTransaction tx,tmptx; uint256 withdrawtxid,tmplasttxid,hashBlock,bindtxid,oracletxid; int32_t numvouts; - uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys,signingpubkeys; + CPubKey mypk,importgatewaypk,signerpk,withdrawpub; struct CCcontract_info *cp,C; char funcid,str[65],burnaddr[64]; int64_t amount; + std::string coin,tmphex; CTransaction tx,tmptx; uint256 withdrawtxid,hashBlock,tokenid,bindtxid,oracletxid; int32_t numvouts; + uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; cp = CCinit(&C,EVAL_IMPORTGATEWAY); - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); + mypk = pubkey2pk(Mypubkey()); importgatewaypk = GetUnspendable(cp,0); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_IMPORTGATEWAY]?0:CC_TXFEE; + txfee = 10000; if (myGetTransaction(lasttxid,tx,hashBlock)==0 || (numvouts= tx.vout.size())<=0 - || (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='S')) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid last txid" << lasttxid.GetHex()); + || (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='P')) + { + CCerror = strprintf("invalid last txid %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if (funcid=='W') { withdrawtxid=lasttxid; - if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,tmppk,coin,withdrawpub,amount)!='W' || refcoin!=coin) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cannot decode withdraw tx opret " << lasttxid.GetHex()); + if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("cannot decode withdraw tx opret %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } else if (myGetTransaction(bindtxid,tmptx,hashBlock)==0 || (numvouts=tmptx.vout.size())<=0) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "can't find bind tx " << bindtxid.GetHex()); + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } else if (KOMODO_EARLYTXID!=zeroid && bindtxid!=KOMODO_EARLYTXID) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid import gateway. On this chain only valid import gateway is "< pubkeys,signingpubkeys; int64_t amount; CPubKey withdrawpub; + CPubKey mypk; struct CCcontract_info *cp,C; char str[65],burnaddr[64]; CTransaction tx; int32_t numvouts; + uint256 withdrawtxid,bindtxid,oracletxid,tokenid,hashBlock; std::string coin,hex; + uint8_t K,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; int64_t amount; CPubKey withdrawpub; cp = CCinit(&C,EVAL_IMPORTGATEWAY); - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); + mypk = pubkey2pk(Mypubkey()); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_IMPORTGATEWAY]?0:CC_TXFEE; - if (myGetTransaction(withdrawsigntxid,tx,hashBlock)==0 || (numvouts= tx.vout.size())<=0) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid withdrawsign txid " << withdrawsigntxid.GetHex()); - else if (DecodeImportGatewayWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)!='S' || refcoin!=coin) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cannot decode withdrawsign tx opret " << withdrawsigntxid.GetHex()); + txfee = 10000; + if (myGetTransaction(completetxid,tx,hashBlock)==0 || (numvouts= tx.vout.size())<=0) + { + CCerror = strprintf("invalid completesigning txid %s",uint256_str(str,completetxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,hex)!='S' || refcoin!=coin) + { + CCerror = strprintf("cannot decode completesigning tx opret %s",uint256_str(str,completetxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (komodo_txnotarizedconfirmed(completetxid)==false) + { + CCerror = strprintf("gatewayscompletesigning tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } else if (myGetTransaction(withdrawtxid,tx,hashBlock)==0 || (numvouts= tx.vout.size())==0) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid withdraw txid " << withdrawtxid.GetHex()); - else if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,tmppk,coin,withdrawpub,amount)!='W' || refcoin!=coin) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cannot decode withdraw tx opret " << withdrawtxid.GetHex()); + { + CCerror = strprintf("invalid withdraw txid %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("cannot decode withdraw tx opret %s\n",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } else if (KOMODO_EARLYTXID!=zeroid && bindtxid!=KOMODO_EARLYTXID) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid import gateway. On this chain only valid import gateway is " << KOMODO_EARLYTXID.GetHex()); + { + CCerror = strprintf("invalid import gateway. On this chain only valid import gateway is %s",KOMODO_EARLYTXID.GetHex()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } else if (myGetTransaction(bindtxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "can't find bind tx " << bindtxid.GetHex()); - else if (DecodeImportGatewayBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin!=coin) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid bind tx " << bindtxid.GetHex()); + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if (AddNormalinputs(mtx,mypk,txfee,3)!=0) { - mtx.vin.push_back(CTxIn(withdrawsigntxid,0,CScript())); + mtx.vin.push_back(CTxIn(completetxid,0,CScript())); mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeImportGatewayMarkDoneOpRet('M',withdrawtxid,mypk,refcoin,withdrawsigntxid))); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeImportGatewayMarkDoneOpRet('M',withdrawtxid,refcoin,completetxid))); } - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "error adding funds for markdone"); + CCerror = strprintf("error adding funds for markdone"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); } -UniValue ImportGatewayPendingSignWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin) +UniValue ImportGatewayPendingWithdraws(uint256 bindtxid,std::string refcoin) { - UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx,withdrawtx; std::string coin,hex; CPubKey mypk,tmppk,importgatewaypk,withdrawpub; - std::vector msigpubkeys; uint256 hashBlock,txid,tmpbindtxid,oracletxid,withdrawtxid,tmplasttxid; uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; - char funcid,burnaddr[65],str[65],depositaddr[65],coinaddr[65],destaddr[65],withaddr[65],numstr[32],signeraddr[65],txidaddr[65]; - int32_t i,n,numvouts,vout,queueflag; int64_t amount,nValue; struct CCcontract_info *cp,C; std::vector signingpubkeys; - std::vector > unspentOutputs; std::vector txs; std::vector tmp_txs; + UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx; std::string coin,hex; CPubKey mypk,importgatewaypk,withdrawpub,signerpk; + std::vector msigpubkeys; uint256 hashBlock,txid,tmpbindtxid,tmptokenid,oracletxid,withdrawtxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; + char funcid,burnaddr[65],coinaddr[65],destaddr[65],str[65],withaddr[65],numstr[32],signeraddr[65],txidaddr[65]; + int32_t i,n,numvouts,vout,queueflag; int64_t amount,nValue; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; cp = CCinit(&C,EVAL_IMPORTGATEWAY); - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); + mypk = pubkey2pk(Mypubkey()); importgatewaypk = GetUnspendable(cp,0); _GetCCaddress(coinaddr,EVAL_IMPORTGATEWAY,importgatewaypk); if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cant find bindtxid " << bindtxid.GetHex()); - if ( DecodeImportGatewayBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid bindtxid " << bindtxid.GetHex() << " coin." << coin); + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } n = msigpubkeys.size(); queueflag = 0; for (i=0; i::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++) - { - tx = *it; - vout=0; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,tx.GetHash(),vout)==0 && (numvouts= tx.vout.size())>0 && tx.vout[vout].nValue == CC_MARKER_VALUE && - DecodeImportGatewayWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)=='S' && myGetTransaction(withdrawtxid,withdrawtx,hashBlock)!=0 - && (numvouts=withdrawtx.vout.size())>0 && DecodeImportGatewayWithdrawOpRet(withdrawtx.vout[numvouts-1].scriptPubKey,tmpbindtxid,tmppk,coin,withdrawpub,amount)=='W' - && refcoin==coin && tmpbindtxid==bindtxid) - { - txs.push_back(tx); - break; - } - } - if (txs.empty()) - { - SetCCunspents(unspentOutputs,coinaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - nValue = (int64_t)it->second.satoshis; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 && vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && - (numvouts= tx.vout.size())>0 && DecodeImportGatewayWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)=='S' && myGetTransaction(withdrawtxid,withdrawtx,hashBlock)!=0 - && (numvouts=withdrawtx.vout.size())>0 && DecodeImportGatewayWithdrawOpRet(withdrawtx.vout[numvouts-1].scriptPubKey,tmpbindtxid,tmppk,coin,withdrawpub,amount)=='W' && refcoin==coin && tmpbindtxid==bindtxid) - { - txs.push_back(tx); - break; - } - } - } - if (txs.empty()) - { - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - nValue = (int64_t)it->second.satoshis; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 && vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && - (numvouts= tx.vout.size())>0 && DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,tmppk,coin,withdrawpub,amount)=='W' && - tmpbindtxid==bindtxid && komodo_get_blocktime(hashBlock)+3600>GetTime()) - { - txs.push_back(tx); - break; - } - } - } - for (std::vector::const_iterator it=txs.begin(); it!=txs.end(); it++) + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - tx = *it; - vout=0; + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; K=0; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,tx.GetHash(),vout) == 0 && tx.vout[vout].nValue == CC_MARKER_VALUE && (numvouts= tx.vout.size())>0 && (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='S')) + if ( vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size())>0 && + (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='P') && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) { - txid=tx.GetHash(); if (funcid=='W') { - DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,tmppk,coin,withdrawpub,amount); - withdrawtxid=txid; + if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,amount)==0 || refcoin!=coin || tmpbindtxid!=bindtxid) continue; } - else if (funcid=='S') + else if (funcid=='P') { - DecodeImportGatewayWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex); - if (myGetTransaction(withdrawtxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0 || DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,tmppk,coin,withdrawpub,amount)!='W') continue; - } + if (DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,hex)!='P' || myGetTransaction(withdrawtxid,tx,hashBlock)==0 + || (numvouts=tx.vout.size())<=0 || DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,amount)!='W' + || refcoin!=coin || tmpbindtxid!=bindtxid) + continue; + } Getscriptaddress(destaddr,tx.vout[1].scriptPubKey); GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); - Getscriptaddress(burnaddr,CScript() << ParseHex(HexStr(CCtxidaddr(str,bindtxid))) << OP_CHECKSIG); - if ( strcmp(destaddr,burnaddr) == 0 ) + if ( strcmp(destaddr,coinaddr) == 0 ) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("withdrawtxid",withdrawtxid.GetHex())); - CCCustomtxidaddr(txidaddr,withdrawtxid,taddr,prefix,prefix2); + obj.push_back(Pair("withdrawtxid",uint256_str(str,tx.GetHash()))); + CCCustomtxidaddr(txidaddr,tx.GetHash(),taddr,prefix,prefix2); obj.push_back(Pair("withdrawtxidaddr",txidaddr)); obj.push_back(Pair("withdrawaddr",withaddr)); sprintf(numstr,"%.8f",(double)tx.vout[1].nValue/COIN); obj.push_back(Pair("amount",numstr)); - obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(withdrawtxid))); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(tx.GetHash()))); if ( queueflag != 0 ) { - obj.push_back(Pair("depositaddr",depositaddr)); + obj.push_back(Pair("depositaddr",burnaddr)); GetCustomscriptaddress(signeraddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG,taddr,prefix,prefix2); obj.push_back(Pair("signeraddr",signeraddr)); } - obj.push_back(Pair("last_txid",txid.GetHex())); - if (funcid=='S' && (std::find(signingpubkeys.begin(),signingpubkeys.end(),mypk)!=signingpubkeys.end() || K>=M) ) obj.push_back(Pair("processed",true)); if (N>1) { obj.push_back(Pair("number_of_signs",K)); + obj.push_back(Pair("last_txid",uint256_str(str,txid))); if (K>0) obj.push_back(Pair("hex",hex)); } pending.push_back(obj); @@ -811,23 +1104,31 @@ UniValue ImportGatewayPendingSignWithdraws(const CPubKey& pk, uint256 bindtxid,s return(result); } -UniValue ImportGatewaySignedWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin) +UniValue ImportGatewayProcessedWithdraws(uint256 bindtxid,std::string refcoin) { - UniValue result(UniValue::VOBJ),processed(UniValue::VARR); CTransaction tx,tmptx,withdrawtx; std::string coin,hex; - CPubKey mypk,tmppk,importgatewaypk,withdrawpub; std::vector msigpubkeys; - uint256 withdrawtxid,tmplasttxid,tmpbindtxid,hashBlock,txid,oracletxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; - char depositaddr[65],signeraddr[65],coinaddr[65],numstr[32],withaddr[65],txidaddr[65]; - int32_t i,n,numvouts,vout,queueflag; int64_t nValue,amount; struct CCcontract_info *cp,C; std::vector signingpubkeys; - std::vector > unspentOutputs; std::vector txs; std::vector tmp_txs; + UniValue result(UniValue::VOBJ),processed(UniValue::VARR); CTransaction tx; std::string coin,hex; + CPubKey mypk,importgatewaypk,withdrawpub; std::vector msigpubkeys; + uint256 withdrawtxid,hashBlock,txid,tmptokenid,oracletxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; + char burnaddr[65],coinaddr[65],str[65],numstr[32],withaddr[65],txidaddr[65]; + int32_t i,n,numvouts,vout,queueflag; int64_t nValue,amount; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; cp = CCinit(&C,EVAL_IMPORTGATEWAY); - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); + mypk = pubkey2pk(Mypubkey()); importgatewaypk = GetUnspendable(cp,0); _GetCCaddress(coinaddr,EVAL_IMPORTGATEWAY,importgatewaypk); if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cant find bindtxid " << bindtxid.GetHex()); - if ( DecodeImportGatewayBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid bindtxid " << bindtxid.GetHex() << " coin." << coin); + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin) + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } n = msigpubkeys.size(); queueflag = 0; for (i=0; i::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++) - { - tx = *it; - vout=0; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,tx.GetHash(),vout) == 0 && (numvouts=tx.vout.size())>0 && tx.vout[vout].nValue == CC_MARKER_VALUE && - DecodeImportGatewayWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)=='S' && K>=M && refcoin==coin && - myGetTransaction(withdrawtxid,withdrawtx,hashBlock) != 0 && (numvouts= withdrawtx.vout.size())>0 && - DecodeImportGatewayWithdrawOpRet(withdrawtx.vout[numvouts-1].scriptPubKey,tmpbindtxid,tmppk,coin,withdrawpub,amount) == 'W' && tmpbindtxid==bindtxid) - txs.push_back(tx); - } - SetCCunspents(unspentOutputs,coinaddr,true); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; nValue = (int64_t)it->second.satoshis; - if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 && vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size())>0 && - DecodeImportGatewayWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)=='S' && K>=M && refcoin==coin && - myGetTransaction(withdrawtxid,withdrawtx,hashBlock) != 0 && (numvouts= withdrawtx.vout.size())>0 && - DecodeImportGatewayWithdrawOpRet(withdrawtx.vout[numvouts-1].scriptPubKey,tmpbindtxid,tmppk,coin,withdrawpub,amount) == 'W' && tmpbindtxid==bindtxid) - txs.push_back(tx); - } - for (std::vector::const_iterator it=txs.begin(); it!=txs.end(); it++) - { - tx = *it; - vout =0; - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,tx.GetHash(),vout) == 0 && (numvouts=tx.vout.size())>0 && DecodeImportGatewayWithdrawSignOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmplasttxid,signingpubkeys,coin,K,hex)=='S' && - K>=M && myGetTransaction(withdrawtxid,withdrawtx,hashBlock) != 0 && (numvouts= withdrawtx.vout.size())>0 && DecodeImportGatewayWithdrawOpRet(withdrawtx.vout[numvouts-1].scriptPubKey,tmpbindtxid,tmppk,coin,withdrawpub,amount) == 'W' && - refcoin==coin && tmpbindtxid==bindtxid) - { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("withdrawsigntxid",tx.GetHash().GetHex())); - obj.push_back(Pair("withdrawtxid",withdrawtxid.GetHex())); - CCCustomtxidaddr(txidaddr,withdrawtxid,taddr,prefix,prefix2); - obj.push_back(Pair("withdrawtxidaddr",txidaddr)); - GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); - obj.push_back(Pair("withdrawaddr",withaddr)); - obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(txid))); - DecodeHexTx(tmptx,hex); - sprintf(numstr,"%.8f",(double)tmptx.vout[0].nValue/COIN); - obj.push_back(Pair("amount",numstr)); - if ( queueflag != 0 ) + if ( vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size())>0 && + DecodeImportGatewayCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,hex) == 'S' && refcoin == coin && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) + { + if (myGetTransaction(withdrawtxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size())>0 + && DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount) == 'W' || refcoin!=coin) { - obj.push_back(Pair("depositaddr",depositaddr)); - GetCustomscriptaddress(signeraddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG,taddr,prefix,prefix2); - obj.push_back(Pair("signeraddr",signeraddr)); + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("completesigningtxid",uint256_str(str,txid))); + obj.push_back(Pair("withdrawtxid",uint256_str(str,withdrawtxid))); + CCCustomtxidaddr(txidaddr,withdrawtxid,taddr,prefix,prefix2); + obj.push_back(Pair("withdrawtxidaddr",txidaddr)); + GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); + obj.push_back(Pair("withdrawaddr",withaddr)); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(txid))); + sprintf(numstr,"%.8f",(double)tx.vout[1].nValue/COIN); + obj.push_back(Pair("amount",numstr)); + obj.push_back(Pair("hex",hex)); + processed.push_back(obj); } - obj.push_back(Pair("last_txid",tx.GetHash().GetHex())); - if (N>1) obj.push_back(Pair("number_of_signs",K)); - if (K>0) obj.push_back(Pair("hex",hex)); - processed.push_back(obj); } } result.push_back(Pair("coin",refcoin)); - result.push_back(Pair("signed",processed)); + result.push_back(Pair("processed",processed)); result.push_back(Pair("queueflag",queueflag)); return(result); } @@ -900,15 +1174,15 @@ UniValue ImportGatewayList() { UniValue result(UniValue::VARR); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid; CTransaction vintx; std::string coin; - char str[65],depositaddr[64]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + char str[65],burnaddr[64]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; cp = CCinit(&C,EVAL_IMPORTGATEWAY); - SetCCtxids(txids,cp->unspendableCCaddr,true,cp->evalcode,CC_MARKER_VALUE,zeroid,'B'); + SetCCtxids(txids,cp->unspendableCCaddr,true,cp->evalcode,zeroid,'B'); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { txid = *it; if ( myGetTransaction(txid,vintx,hashBlock) != 0 ) { - if ( vintx.vout.size() > 0 && DecodeImportGatewayBindOpRet(depositaddr,vintx.vout[vintx.vout.size()-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 0 ) + if ( vintx.vout.size() > 0 && DecodeImportGatewayBindOpRet(burnaddr,vintx.vout[vintx.vout.size()-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 0 ) { result.push_back(uint256_str(str,txid)); } @@ -920,13 +1194,21 @@ UniValue ImportGatewayList() UniValue ImportGatewayExternalAddress(uint256 bindtxid,CPubKey pubkey) { UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid; CTransaction tx; - std::string coin; int64_t numvouts; char addr[65],depositaddr[65]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector msigpubkeys; + std::string coin; int64_t numvouts; char str[65],addr[65],burnaddr[65]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector msigpubkeys; cp = CCinit(&C,EVAL_IMPORTGATEWAY); if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cant find bindtxid " << bindtxid.GetHex()); - if ( DecodeImportGatewayBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid bindtxid " << bindtxid.GetHex() << " coin." << coin); + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } GetCustomscriptaddress(addr,CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG,taddr,prefix,prefix2); result.push_back(Pair("result","success")); result.push_back(Pair("address",addr)); @@ -936,13 +1218,22 @@ UniValue ImportGatewayExternalAddress(uint256 bindtxid,CPubKey pubkey) UniValue ImportGatewayDumpPrivKey(uint256 bindtxid,CKey key) { UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid; CTransaction tx; - std::string coin,priv; int64_t numvouts; char addr[65],depositaddr[65]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector msigpubkeys; + std::string coin,priv; int64_t numvouts; char str[65],addr[65],burnaddr[65]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector msigpubkeys; cp = CCinit(&C,EVAL_IMPORTGATEWAY); if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cant find bindtxid " << bindtxid.GetHex()); - if ( DecodeImportGatewayBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid bindtxid " << bindtxid.GetHex() << " coin." << coin); + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + priv=EncodeCustomSecret(key,wiftype); result.push_back(Pair("result","success")); result.push_back(Pair("privkey",priv.c_str())); @@ -952,22 +1243,31 @@ UniValue ImportGatewayDumpPrivKey(uint256 bindtxid,CKey key) UniValue ImportGatewayInfo(uint256 bindtxid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::string coin; char str[67],numstr[65],depositaddr[64]; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::string coin; char str[67],numstr[65],burnaddr[64],gatewaystokens[64]; uint8_t M,N; std::vector pubkeys; uint8_t taddr,prefix,prefix2,wiftype; uint256 oracletxid,hashBlock; CTransaction tx; CPubKey ImportGatewaypk; struct CCcontract_info *cp,C; int32_t i; int64_t numvouts,remaining; std::vector msigpubkeys; cp = CCinit(&C,EVAL_IMPORTGATEWAY); ImportGatewaypk = GetUnspendable(cp,0); + GetTokensCCaddress(cp,gatewaystokens,ImportGatewaypk); if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "cant find bindtxid " << bindtxid.GetHex()); - if ( DecodeImportGatewayBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') - CCERR_RESULT("importgateway",CCLOG_ERROR, stream << "invalid bindtxid " << bindtxid.GetHex() << " coin." << coin); + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if ( myGetTransaction(bindtxid,tx,hashBlock) != 0 ) { result.push_back(Pair("result","success")); result.push_back(Pair("name","ImportGateway")); - depositaddr[0] = 0; - if ( tx.vout.size() > 0 && DecodeImportGatewayBindOpRet(depositaddr,tx.vout[tx.vout.size()-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 0 && M <= N && N > 0 ) + burnaddr[0] = 0; + if ( tx.vout.size() > 0 && DecodeImportGatewayBindOpRet(burnaddr,tx.vout[tx.vout.size()-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 0 && M <= N && N > 0 ) { result.push_back(Pair("M",M)); result.push_back(Pair("N",N)); @@ -980,7 +1280,7 @@ UniValue ImportGatewayInfo(uint256 bindtxid) result.push_back(Pair("prefix",prefix)); result.push_back(Pair("prefix2",prefix2)); result.push_back(Pair("wiftype",wiftype)); - result.push_back(Pair("depositaddr",depositaddr)); + result.push_back(Pair("deposit",burnaddr)); } } return(result); diff --git a/src/cc/includes/curve25519.h b/src/cc/includes/curve25519.h index 422fd7744c1..657fab4d011 100755 --- a/src/cc/includes/curve25519.h +++ b/src/cc/includes/curve25519.h @@ -81,7 +81,7 @@ void calc_hmac_sha256(uint8_t *mac,int32_t maclen,uint8_t *key,int32_t key_size, #include "../includes/tweetnacl.h" int32_t _SuperNET_cipher(uint8_t nonce[crypto_box_NONCEBYTES],uint8_t *cipher,uint8_t *message,int32_t len,bits256 destpub,bits256 srcpriv,uint8_t *buf); uint8_t *_SuperNET_decipher(uint8_t nonce[crypto_box_NONCEBYTES],uint8_t *cipher,uint8_t *message,int32_t len,bits256 srcpub,bits256 mypriv); -//void *SuperNET_deciphercalc(void **ptrp,int32_t *msglenp,bits256 privkey,bits256 srcpubkey,uint8_t *cipher,int32_t cipherlen,uint8_t *buf,int32_t bufsize); -//uint8_t *SuperNET_ciphercalc(void **ptrp,int32_t *cipherlenp,bits256 *privkeyp,bits256 *destpubkeyp,uint8_t *data,int32_t datalen,uint8_t *space2,int32_t space2size); +void *SuperNET_deciphercalc(void **ptrp,int32_t *msglenp,bits256 privkey,bits256 srcpubkey,uint8_t *cipher,int32_t cipherlen,uint8_t *buf,int32_t bufsize); +uint8_t *SuperNET_ciphercalc(void **ptrp,int32_t *cipherlenp,bits256 *privkeyp,bits256 *destpubkeyp,uint8_t *data,int32_t datalen,uint8_t *space2,int32_t space2size); #endif diff --git a/src/cc/kogs.cpp b/src/cc/kogs.cpp deleted file mode 100644 index 3ec4e5aa7dc..00000000000 --- a/src/cc/kogs.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/****************************************************************************** -* Copyright 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ - -#include "CCKogs.h" - - -bool KogsValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) -{ - return eval->Invalid("not supported yet"); -} - diff --git a/src/cc/lotto.cpp b/src/cc/lotto.cpp index f2d8b75287b..db441444564 100644 --- a/src/cc/lotto.cpp +++ b/src/cc/lotto.cpp @@ -58,29 +58,6 @@ It is possible to have a jackpot but miss out on it due to not claiming it. To m lottotickets lottostatus lottowinner tickethash ticketid - - third iteration: - - with the inclusion of the nist random numbers, a much simpler lotto can be created. - - anybody can create a new lotto by sending funds to a CC address with a deadline specified. - until the deadline, anybody can add funds to specific lotto CC address - after the tx that notarizes the deadline is notarized, the winner can spend all the funds sent (one utxo at a time or all at once). - - the winner is decided using a deterministic algorithm between all addresses that paid into the lotto: - - addr0 funds0 - addr1 funds1 - ... - addrN fundsN - - a total of sum(funds0 ... fundsN) "tickets" are created and each addrX gets a proportional chance to win based on the NIST data included in the first block after the notarization of the notarization tx. this algo should be reasonably efficient to calculate and will determine the winning pubkey. - -and with the consensus on which pubkey won that specific lotto, only that pubkey would be allowed to spend the funds. - - [to prevent funds from being locked in a lotto CC address due to unclaimed, the exclusive right can be for a limted time. after this limited time, then a new lotto can be created that will include all of the unclaimed funds. once that new lotto create tx is notarized, then the winning pubkey for the old lotto will not be able to spend the old lotto funds] - - */ @@ -289,7 +266,7 @@ UniValue LottoList() { UniValue result(UniValue::VARR); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,hentropy; CTransaction vintx; uint64_t sbits; int32_t ticketsize,odds,firstheight,period; char str[65]; cp = CCinit(&C,EVAL_LOTTO); - SetCCtxids(txids,cp->normaladdr,true,cp->evalcode,0,zeroid,'F'); + SetCCtxids(txids,cp->normaladdr,true,cp->evalcode,zeroid,'F'); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { txid = *it; diff --git a/src/cc/marmara.cpp b/src/cc/marmara.cpp index b7db8219bb1..68625c0382b 100644 --- a/src/cc/marmara.cpp +++ b/src/cc/marmara.cpp @@ -1,4 +1,3 @@ -/****************************************************************************** /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -15,3674 +14,1035 @@ ******************************************************************************/ #include "CCMarmara.h" -#include "key_io.h" - - /* - Marmara CC is for the MARMARA project - - MARMARA_CREATELOOP initial data for credit loop - vins normal - vout0 request to senderpk (issuer) - - MARMARA_REQUEST request for credit issuance - vins normal - vout0 request to senderpk (endorser) - - MARMARA_ISSUE check issuance - vin0 request from MARMARA_REQUEST - vins1+ normal - vout0 baton to 1st receiverpk - vout1 marker to Marmara so all issuances can be tracked (spent when loop is closed) - - MARMARA_TRANSFER check transfer to endorser - vin0 request from MARMARA_REQUEST - vin1 baton from MARMARA_ISSUE/MARMARA_TRANSFER - vins2+ normal - vout0 baton to next receiverpk (following the unspent baton back to original is the credit loop) - - MARMARA_SETTLE check settlement - vin0 MARMARA_ISSUE marker - vin1 baton - vins CC utxos from credit loop - - MARMARA_SETTLE_PARTIAL default/partial payment in the settlement - //TODO: should we implement several partial settlements in case of too many vins? - - MARMARA_ACTIVATED activated funds - MARMARA_ACTIVATED_3X activated funds with 3x stake advantage -- not used - - MARMARA_COINBASE marmara coinbase - MARMARA_COINBASE_3X marmara coinbase with 3x stake advantage - - MARMARA_LOOP lock in loop last vout opret - - MARMARA_LOCKED locked-in-loop cc vout opret with the pubkey which locked his funds in this vout - - */ - - -const bool CHECK_ONLY_CCOPRET = true; - -// credit loop data structure allowing to store data from different LCL tx oprets -struct SMarmaraCreditLoopOpret { - bool hasCreateOpret; - bool hasIssuanceOpret; - bool hasSettlementOpret; - - uint8_t lastfuncid; - - uint8_t autoSettlement; - uint8_t autoInsurance; - - // create tx data: - CAmount amount; // loop amount - int32_t matures; // check maturing height - std::string currency; // currently MARMARA - - // issuer data: - int32_t disputeExpiresHeight; - uint8_t escrowOn; - CAmount blockageAmount; - - // last issuer/endorser/receiver data: - uint256 createtxid; - CPubKey pk; // always the last pk in opret - int32_t avalCount; // only for issuer/endorser - - // settlement data: - CAmount remaining; - - // init default values: - SMarmaraCreditLoopOpret() { - hasCreateOpret = false; - hasIssuanceOpret = false; - hasSettlementOpret = false; - - lastfuncid = 0; - - amount = 0LL; - matures = 0; - autoSettlement = 1; - autoInsurance = 1; - - createtxid = zeroid; - disputeExpiresHeight = 0; - avalCount = 0; - escrowOn = false; - blockageAmount = 0LL; - - remaining = 0L; - } -}; - - -// Classes to check opret by calling CheckOpret member func for two cases: -// 1) the opret in cc vout data is checked first and considered primary -// 2) if it is not required to check only cc opret, the opret in the last vout is checked second and considered secondary -// returns the opret and pubkey from the opret - -class CMarmaraOpretCheckerBase { -public: - bool checkOnlyCC; - virtual bool CheckOpret(const CScript &spk, CPubKey &opretpk) const = 0; -}; - -// checks if opret for activated coins, returns pk from opret -class CMarmaraActivatedOpretChecker : public CMarmaraOpretCheckerBase -{ -public: - CMarmaraActivatedOpretChecker() { checkOnlyCC = true; } // only the cc opret allowed now - // CActivatedOpretChecker(bool onlyCC) { checkOnlyCC = onlyCC; } - bool CheckOpret(const CScript &spk, CPubKey &opretpk) const - { - uint8_t funcid; - int32_t ht, unlockht; - - return MarmaraDecodeCoinbaseOpret(spk, opretpk, ht, unlockht) != 0; - } -}; - -// checks if opret for lock-in-loop coins, returns pk from opret -class CMarmaraLockInLoopOpretChecker : public CMarmaraOpretCheckerBase -{ -public: - CMarmaraLockInLoopOpretChecker() { checkOnlyCC = false; } - CMarmaraLockInLoopOpretChecker(bool onlyCC) { checkOnlyCC = onlyCC; } - bool CheckOpret(const CScript &spk, CPubKey &opretpk) const - { - struct SMarmaraCreditLoopOpret loopData; - - uint8_t funcid = MarmaraDecodeLoopOpret(spk, loopData); - if (funcid != 0) { - opretpk = loopData.pk; - return true; - } - return false; - } -}; +/* + Marmara CC is for the MARMARA project + + 'R': two forms for initial issuance and for accepting existing + vins normal + vout0 approval to senderpk (issuer or owner of baton) + + 'I' + vin0 approval from 'R' + vins1+ normal + vout0 baton to 1st receiverpk + vout1 marker to Marmara so all issuances can be tracked (spent when loop is closed) + + 'T' + vin0 approval from 'R' + vin1 baton from 'I'/'T' + vins2+ normal + vout0 baton to next receiverpk (following the unspent baton back to original is the credit loop) + + 'S' + vin0 'I' marker + vin1 baton + vins CC utxos from credit loop + + 'D' default/partial payment + + 'L' lockfunds + +*/ -// helper functions for rpc calls +// start of consensus code -/* see similar funcs below -int64_t IsMarmaravout(struct CCcontract_info *cp, const CTransaction& tx, int32_t v) +int64_t IsMarmaravout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) { - char destaddr[KOMODO_ADDRESS_BUFSIZE]; - if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) + char destaddr[64]; + if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) { - if (Getscriptaddress(destaddr, tx.vout[v].scriptPubKey) && strcmp(destaddr, cp->unspendableCCaddr) == 0) + if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) return(tx.vout[v].nValue); } return(0); -}*/ +} -// Get randomized within range [3 month...2 year] using ind as seed(?) -/* not used now int32_t MarmaraRandomize(uint32_t ind) { - uint64_t val64; uint32_t val, range = (MARMARA_MAXLOCK - MARMARA_MINLOCK); + uint64_t val64; uint32_t val,range = (MARMARA_MAXLOCK - MARMARA_MINLOCK); val64 = komodo_block_prg(ind); val = (uint32_t)(val64 >> 32); val ^= (uint32_t)val64; return((val % range) + MARMARA_MINLOCK); } -*/ -// get random but fixed for the height param unlock height within 3 month..2 year interval -- discontinued -// now always returns maxheight int32_t MarmaraUnlockht(int32_t height) { - /* uint32_t ind = height / MARMARA_GROUPSIZE; + uint32_t ind = height / MARMARA_GROUPSIZE; height = (height / MARMARA_GROUPSIZE) * MARMARA_GROUPSIZE; - return(height + MarmaraRandomize(ind)); */ - return MARMARA_V2LOCKHEIGHT; + return(height + MarmaraRandomize(ind)); } -uint8_t MarmaraDecodeCoinbaseOpret(const CScript &scriptPubKey, CPubKey &pk, int32_t &height, int32_t &unlockht) +uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32_t &height,int32_t &unlockht) { - vscript_t vopret; - GetOpReturnData(scriptPubKey, vopret); - - if (vopret.size() >= 3) + std::vector vopret; uint8_t *script,e,f,funcid; + GetOpReturnData(scriptPubKey,vopret); + script = (uint8_t *)vopret.data(); + if ( 0 ) { - uint8_t evalcode, funcid, version; - uint8_t *script = (uint8_t *)vopret.data(); - - if (script[0] == EVAL_MARMARA) + int32_t i; + for (i=0; i 2 && script[0] == EVAL_MARMARA ) + { + if ( script[1] == 'C' || script[1] == 'P' || script[1] == 'L' ) { - if (IsFuncidOneOf(script[1], MARMARA_ACTIVATED_FUNCIDS)) + if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> height; ss >> unlockht) != 0 ) { - if (script[2] == MARMARA_OPRET_VERSION) - { - if (E_UNMARSHAL(vopret, ss >> evalcode; ss >> funcid; ss >> version; ss >> pk; ss >> height; ss >> unlockht) != 0) - { - return(script[1]); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "opret unmarshal error for funcid=" << (char)script[1] << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "incorrect marmara activated or coinbase opret version=" << (char)script[2] << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "not marmara activated or coinbase funcid=" << (char)script[1] << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "not marmara opret, evalcode=" << (int)script[0] << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "bad marmara opret, vopret.size()=" << vopret.size() << std::endl); + return(script[1]); + } else fprintf(stderr,"DecodeMaramaraCoinbaseOpRet unmarshal error for %c\n",script[1]); + } //else fprintf(stderr,"script[1] is %d != 'C' %d or 'P' %d or 'L' %d\n",script[1],'C','P','L'); + } else fprintf(stderr,"vopret.size() is %d\n",(int32_t)vopret.size()); return(0); } -CScript EncodeMarmaraCoinbaseOpRet(uint8_t funcid, CPubKey pk, int32_t ht) +CScript EncodeMarmaraCoinbaseOpRet(uint8_t funcid,CPubKey pk,int32_t ht) { - CScript opret; - int32_t unlockht; - uint8_t evalcode = EVAL_MARMARA; - uint8_t version = MARMARA_OPRET_VERSION; - + CScript opret; int32_t unlockht; uint8_t evalcode = EVAL_MARMARA; unlockht = MarmaraUnlockht(ht); - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << version << pk << ht << unlockht); - /* if (0) - { - vscript_t vopret; uint8_t *script, i; - GetOpReturnData(opret, vopret); - script = (uint8_t *)vopret.data(); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << pk << ht << unlockht); + if ( 0 ) { - std::cerr << " "; - for (i = 0; i < vopret.size(); i++) - fprintf(stderr, "%02x", script[i]); - fprintf(stderr, " <- gen opret.%c\n", funcid); + std::vector vopret; uint8_t *script,i; + GetOpReturnData(opret,vopret); + script = (uint8_t *)vopret.data(); + { + for (i=0; i vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> createtxid; ss >> senderpk; ss >> amount; ss >> matures; ss >> currency) != 0 ) + { + return(f); + } + return(0); } -CScript MarmaraEncodeLoopRequestOpret(uint256 createtxid, CPubKey senderpk) +int32_t MarmaraGetcreatetxid(uint256 &createtxid,uint256 txid) { - CScript opret; - uint8_t evalcode = EVAL_MARMARA; - uint8_t funcid = MARMARA_REQUEST; // request tx - uint8_t version = MARMARA_OPRET_VERSION; - - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << version << createtxid << senderpk); - return(opret); + CTransaction tx; uint256 hashBlock; uint8_t funcid; int32_t numvouts,matures; std::string currency; CPubKey senderpk; int64_t amount; + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + if ( (funcid= MarmaraDecodeLoopOpret(tx.vout[numvouts-1].scriptPubKey,createtxid,senderpk,amount,matures,currency)) == 'I' || funcid == 'T' ) + return(0); + else if ( funcid == 'R' ) + { + if ( createtxid == zeroid ) + createtxid = txid; + return(0); + } + } + return(-1); } -CScript MarmaraEncodeLoopTransferOpret(uint256 createtxid, CPubKey receiverpk, int32_t avalCount) +int32_t MarmaraGetbatontxid(std::vector &creditloop,uint256 &batontxid,uint256 txid) { - CScript opret; - uint8_t evalcode = EVAL_MARMARA; - uint8_t funcid = MARMARA_TRANSFER; // transfer tx - uint8_t version = MARMARA_OPRET_VERSION; - - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << version << createtxid << receiverpk << avalCount); - return(opret); + uint256 createtxid,spenttxid; int64_t value; int32_t vini,height,n=0,vout = 0; + memset(&batontxid,0,sizeof(batontxid)); + if ( MarmaraGetcreatetxid(createtxid,txid) == 0 ) + { + txid = createtxid; + //fprintf(stderr,"txid.%s -> createtxid %s\n",txid.GetHex().c_str(),createtxid.GetHex().c_str()); + while ( CCgetspenttxid(spenttxid,vini,height,txid,vout) == 0 ) + { + creditloop.push_back(txid); + //fprintf(stderr,"%d: %s\n",n,txid.GetHex().c_str()); + n++; + if ( (value= CCgettxout(spenttxid,vout,1,1)) == 10000 ) + { + batontxid = spenttxid; + //fprintf(stderr,"got baton %s %.8f\n",batontxid.GetHex().c_str(),(double)value/COIN); + return(n); + } + else if ( value > 0 ) + { + batontxid = spenttxid; + fprintf(stderr,"n.%d got false baton %s/v%d %.8f\n",n,batontxid.GetHex().c_str(),vout,(double)value/COIN); + return(n); + } + // get funcid + txid = spenttxid; + } + } + return(-1); } -CScript MarmaraEncodeLoopCCVoutOpret(uint256 createtxid, CPubKey senderpk) +CScript Marmara_scriptPubKey(int32_t height,CPubKey pk) { - CScript opret; - uint8_t evalcode = EVAL_MARMARA; - uint8_t funcid = MARMARA_LOCKED; // opret in cc 1of2 lock-in-loop vout - uint8_t version = MARMARA_OPRET_VERSION; - - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << version << createtxid << senderpk); - return(opret); + CTxOut ccvout; struct CCcontract_info *cp,C; CPubKey Marmarapk; + cp = CCinit(&C,EVAL_MARMARA); + Marmarapk = GetUnspendable(cp,0); + if ( height > 0 && (height & 1) == 0 && pk.size() == 33 ) + { + ccvout = MakeCC1of2vout(EVAL_MARMARA,0,Marmarapk,pk); + //char coinaddr[64]; + //Getscriptaddress(coinaddr,ccvout.scriptPubKey); + //fprintf(stderr,"Marmara_scriptPubKey %s ht.%d -> %s\n",HexStr(pk).c_str(),height,coinaddr); + } + return(ccvout.scriptPubKey); } -CScript MarmaraEncodeLoopSettlementOpret(bool isSuccess, uint256 createtxid, CPubKey pk, CAmount remaining) +CScript MarmaraCoinbaseOpret(uint8_t funcid,int32_t height,CPubKey pk) { - CScript opret; - uint8_t evalcode = EVAL_MARMARA; - uint8_t funcid = isSuccess ? MARMARA_SETTLE : MARMARA_SETTLE_PARTIAL; - uint8_t version = MARMARA_OPRET_VERSION; - - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << version << createtxid << pk << remaining); - return(opret); + uint8_t *ptr; + //fprintf(stderr,"height.%d pksize.%d\n",height,(int32_t)pk.size()); + if ( height > 0 && (height & 1) == 0 && pk.size() == 33 ) + return(EncodeMarmaraCoinbaseOpRet(funcid,pk,height)); + return(CScript()); } -// decode different lock-in-loop oprets, update the loopData -uint8_t MarmaraDecodeLoopOpret(const CScript scriptPubKey, struct SMarmaraCreditLoopOpret &loopData) +int32_t MarmaraValidateCoinbase(int32_t height,CTransaction tx) { - vscript_t vopret; - - GetOpReturnData(scriptPubKey, vopret); - if (vopret.size() >= 3) + struct CCcontract_info *cp,C; CPubKey Marmarapk,pk; int32_t ht,unlockht; CTxOut ccvout; + cp = CCinit(&C,EVAL_MARMARA); + Marmarapk = GetUnspendable(cp,0); + if ( 0 ) + { + int32_t d,histo[365*2+30]; + memset(histo,0,sizeof(histo)); + for (ht=2; ht<100; ht++) + fprintf(stderr,"%d ",MarmaraUnlockht(ht)); + fprintf(stderr," <- first 100 unlock heights\n"); + for (ht=2; ht<1000000; ht+=MARMARA_GROUPSIZE) + { + d = (MarmaraUnlockht(ht) - ht) / 1440; + if ( d < 0 || d > sizeof(histo)/sizeof(*histo) ) + fprintf(stderr,"d error.%d at ht.%d\n",d,ht); + else histo[d]++; + } + for (ht=0; ht> evalcode; ss >> loopData.lastfuncid; ss >> version; ss >> loopData.pk; ss >> loopData.amount; ss >> loopData.matures; ss >> loopData.currency)) { - loopData.hasCreateOpret = true; - return loopData.lastfuncid; - } - } - else if (funcid == MARMARA_ISSUE) { - if (E_UNMARSHAL(vopret, ss >> evalcode; ss >> loopData.lastfuncid; ss >> version; ss >> loopData.createtxid; ss >> loopData.pk; ss >> loopData.autoSettlement; ss >> loopData.autoInsurance; ss >> loopData.avalCount >> loopData.disputeExpiresHeight >> loopData.escrowOn >> loopData.blockageAmount)) { - loopData.hasIssuanceOpret = true; - return loopData.lastfuncid; - } - } - else if (funcid == MARMARA_REQUEST) { - if (E_UNMARSHAL(vopret, ss >> evalcode; ss >> loopData.lastfuncid; ss >> version; ss >> loopData.createtxid; ss >> loopData.pk)) { - return funcid; - } - } - else if (funcid == MARMARA_TRANSFER) { - if (E_UNMARSHAL(vopret, ss >> evalcode; ss >> loopData.lastfuncid; ss >> version; ss >> loopData.createtxid; ss >> loopData.pk; ss >> loopData.avalCount)) { - return funcid; - } - } - else if (funcid == MARMARA_LOCKED) { - if (E_UNMARSHAL(vopret, ss >> evalcode; ss >> loopData.lastfuncid; ss >> version; ss >> loopData.createtxid; ss >> loopData.pk)) { - return funcid; - } - } - else if (funcid == MARMARA_SETTLE || funcid == MARMARA_SETTLE_PARTIAL) { - if (E_UNMARSHAL(vopret, ss >> evalcode; ss >> loopData.lastfuncid; ss >> version; ss >> loopData.createtxid; ss >> loopData.pk >> loopData.remaining)) { - loopData.hasSettlementOpret = true; - return funcid; - } - } - // get here from any E_UNMARSHAL error: - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "cannot parse loop opret: not my funcid=" << (int)funcid << " or bad opret format=" << HexStr(vopret) << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "unsupported opret version=" << (int)version << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "not marmara opret, evalcode=" << (int)evalcode << std::endl); + //fprintf(stderr,"ht.%d -> unlock.%d\n",ht,unlockht); + ccvout = MakeCC1of2vout(EVAL_MARMARA,0,Marmarapk,pk); + if ( ccvout.scriptPubKey == tx.vout[0].scriptPubKey ) + return(0); + char addr0[64],addr1[64]; + Getscriptaddress(addr0,ccvout.scriptPubKey); + Getscriptaddress(addr1,tx.vout[0].scriptPubKey); + fprintf(stderr,"ht.%d mismatched CCvout scriptPubKey %s vs %s pk.%d %s\n",height,addr0,addr1,(int32_t)pk.size(),HexStr(pk).c_str()); + } else fprintf(stderr,"ht.%d %d vs %d unlock.%d\n",height,MarmaraUnlockht(height),ht,unlockht); + } else fprintf(stderr,"ht.%d error decoding coinbase opret\n",height); } - else - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "opret too small=" << HexStr(vopret) << std::endl); - - return(0); + return(-1); } -static CTxOut MakeMarmaraCC1of2voutOpret(CAmount amount, const CPubKey &pk2, const CScript &opret) +bool MarmaraPoScheck(char *destaddr,CScript opret,CTransaction staketx) { - vscript_t vopret; - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - CPubKey Marmarapk = GetUnspendable(cp, 0); - - GetOpReturnData(opret, vopret); - if (!vopret.empty()) { - std::vector< vscript_t > vData{ vopret }; // add mypk to vout to identify who has locked coins in the credit loop - return MakeCC1of2vout(EVAL_MARMARA, amount, Marmarapk, pk2, &vData); + CPubKey Marmarapk,pk; int32_t height,unlockht; uint8_t funcid; char coinaddr[64]; struct CCcontract_info *cp,C; + //fprintf(stderr,"%s numvins.%d numvouts.%d %.8f opret[%d]\n",staketx.GetHash().ToString().c_str(),(int32_t)staketx.vin.size(),(int32_t)staketx.vout.size(),(double)staketx.vout[0].nValue/COIN,(int32_t)opret.size()); + if ( staketx.vout.size() == 2 && opret == staketx.vout[1].scriptPubKey ) + { + cp = CCinit(&C,EVAL_MARMARA); + funcid = DecodeMaramaraCoinbaseOpRet(opret,pk,height,unlockht); + Marmarapk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,Marmarapk,pk); + //fprintf(stderr,"matched opret! funcid.%c ht.%d unlock.%d %s\n",funcid,height,unlockht,coinaddr); + return(strcmp(destaddr,coinaddr) == 0); } - else - return MakeCC1of2vout(EVAL_MARMARA, amount, Marmarapk, pk2, NULL); + return(0); } -bool MyGetCCopret(const CScript &scriptPubKey, CScript &opret) +bool MarmaraValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - std::vector> vParams; - CScript dummy; - - if (scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) != 0) + std::vector vopret; CTransaction vinTx; uint256 hashBlock; int32_t numvins,numvouts,i,ht,unlockht,vht,vunlockht; uint8_t funcid,vfuncid,*script; CPubKey pk,vpk; + if ( ASSETCHAINS_MARMARA == 0 ) + return eval->Invalid("-ac_marmara must be set for marmara CC"); + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + if ( numvouts < 1 ) + return eval->Invalid("no vouts"); + else if ( tx.vout.size() >= 2 ) { - if (vParams.size() == 1) + GetOpReturnData(tx.vout[tx.vout.size()-1].scriptPubKey,vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() < 2 || script[0] != EVAL_MARMARA ) + return eval->Invalid("no opreturn"); + funcid = script[1]; + if ( funcid == 'P' ) { - //uint8_t version; - //uint8_t evalCode; - //uint8_t m, n; - vscript_t vheader; - std::vector< vscript_t > vData; - - E_UNMARSHAL(vParams[0], \ - ss >> vheader; \ - while (!ss.eof()) \ - { \ - vscript_t velem; \ - ss >> velem; \ - vData.push_back(velem); \ - }); - - if (vData.size() > 0) + funcid = DecodeMaramaraCoinbaseOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,pk,ht,unlockht); + for (i=0; iismyvin)(tx.vin[i].scriptSig) != 0 ) + { + if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) + return eval->Invalid("cant find vinTx"); + else + { + if ( vinTx.IsCoinBase() == 0 ) + return eval->Invalid("noncoinbase input"); + else if ( vinTx.vout.size() != 2 ) + return eval->Invalid("coinbase doesnt have 2 vouts"); + vfuncid = DecodeMaramaraCoinbaseOpRet(vinTx.vout[1].scriptPubKey,vpk,vht,vunlockht); + if ( vfuncid != 'C' || vpk != pk || vunlockht != unlockht ) + return eval->Invalid("mismatched opreturn"); + } + } } + return(true); + } + else if ( funcid == 'L' ) // lock -> lock funds with a unlockht + { + return(true); + } + else if ( funcid == 'R' ) // receive -> agree to receive 'I' from pk, amount, currency, dueht + { + return(true); } + else if ( funcid == 'I' ) // issue -> issue currency to pk with due date height + { + return(true); + } + else if ( funcid == 'T' ) // transfer -> given 'R' transfer 'I' or 'T' to the pk of 'R' + { + return(true); + } + else if ( funcid == 'S' ) // settlement -> automatically spend issuers locked funds, given 'I' + { + return(true); + } + else if ( funcid == 'D' ) // insufficient settlement + { + return(true); + } + else if ( funcid == 'C' ) // coinbase + { + return(true); + } + // staking only for locked utxo } - return false; + return eval->Invalid("fall through error"); } +// end of consensus code -static bool GetCCOpReturnData(const CScript &spk, CScript &opret) -{ - CScript dummy; - std::vector< vscript_t > vParams; - - return MyGetCCopret(spk, opret); - - // get cc opret - /* if (spk.IsPayToCryptoCondition(&dummy, vParams)) - { - if (vParams.size() > 0) - { - COptCCParams p = COptCCParams(vParams[0]); - if (p.vData.size() > 0) - { - opret << OP_RETURN << p.vData[0]; // reconstruct opret - return true; - } - } - }*/ - return false; -} +// helper functions for rpc calls in rpcwallet.cpp -// add mined coins -int64_t AddMarmaraCoinbases(struct CCcontract_info *cp, CMutableTransaction &mtx, int32_t firstheight, CPubKey poolpk, int32_t maxinputs) +int64_t AddMarmaraCoinbases(struct CCcontract_info *cp,CMutableTransaction &mtx,int32_t firstheight,CPubKey poolpk,int32_t maxinputs) { - char coinaddr[KOMODO_ADDRESS_BUFSIZE]; - int64_t nValue, totalinputs = 0; - uint256 txid, hashBlock; - CTransaction vintx; - int32_t unlockht, ht, vout, unlocks, n = 0; + char coinaddr[64]; CPubKey Marmarapk,pk; int64_t nValue,totalinputs = 0; uint256 txid,hashBlock; CTransaction vintx; int32_t unlockht,ht,vout,unlocks,n = 0; std::vector > unspentOutputs; - - CPubKey Marmarapk = GetUnspendable(cp, NULL); - GetCCaddress1of2(cp, coinaddr, Marmarapk, poolpk); - SetCCunspents(unspentOutputs, coinaddr, true); + Marmarapk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,Marmarapk,poolpk); + SetCCunspents(unspentOutputs,coinaddr,true); unlocks = MarmaraUnlockht(firstheight); - - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << " check coinaddr=" << coinaddr << std::endl); - for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) + //fprintf(stderr,"check coinaddr.(%s)\n",coinaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << " txid=" << txid.GetHex() << " vout=" << vout << std::endl); - if (myGetTransaction(txid, vintx, hashBlock) != 0) + //fprintf(stderr,"txid.%s/v%d\n",txid.GetHex().c_str(),vout); + if ( myGetTransaction(txid,vintx,hashBlock) != 0 ) { - if (vintx.IsCoinBase() != 0 && vintx.vout.size() == 2 && vintx.vout[1].nValue == 0) + if ( vintx.IsCoinBase() != 0 && vintx.vout.size() == 2 && vintx.vout[1].nValue == 0 ) { - CPubKey pk; - if (MarmaraDecodeCoinbaseOpret(vintx.vout[1].scriptPubKey, pk, ht, unlockht) == MARMARA_COINBASE && unlockht == unlocks && pk == poolpk && ht >= firstheight) + if ( DecodeMaramaraCoinbaseOpRet(vintx.vout[1].scriptPubKey,pk,ht,unlockht) == 'C' && unlockht == unlocks && pk == poolpk && ht >= firstheight ) { - if ((nValue = vintx.vout[vout].nValue) > 0 && myIsutxo_spentinmempool(ignoretxid, ignorevin, txid, vout) == 0) + if ( (nValue= vintx.vout[vout].nValue) > 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { - if (maxinputs != 0) - mtx.vin.push_back(CTxIn(txid, vout, CScript())); + if ( maxinputs != 0 ) + mtx.vin.push_back(CTxIn(txid,vout,CScript())); nValue = it->second.satoshis; totalinputs += nValue; n++; - if (maxinputs > 0 && n >= maxinputs) + if ( maxinputs > 0 && n >= maxinputs ) break; - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "tx in mempool or vout not positive, nValue=" << nValue << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "decode error unlockht=" << unlockht << " vs unlocks=" << unlocks << " is-pool-pk=" << (pk == poolpk) << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "not coinbase" << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "error getting tx=" << txid.GetHex() << std::endl); + } //else fprintf(stderr,"nValue.%8f\n",(double)nValue/COIN); + } //else fprintf(stderr,"decode error unlockht.%d vs %d pk.%d\n",unlockht,unlocks,pk == poolpk); + } else fprintf(stderr,"not coinbase\n"); + } else fprintf(stderr,"error getting tx\n"); } return(totalinputs); } - -// checks either of two options for tx: -// tx has cc vin for evalcode -static bool tx_has_my_cc_vin(struct CCcontract_info *cp, const CTransaction &tx) -{ - for (auto const &vin : tx.vin) - if (cp->ismyvin(vin.scriptSig)) - return true; - - return false; -} - -// check if this is a activated vout: -static bool activated_vout_matches_pk_in_opret(const CTransaction &tx, int32_t nvout, const CScript &opret) -{ - CPubKey pk; - int32_t h, unlockh; - - MarmaraDecodeCoinbaseOpret(opret, pk, h, unlockh); - if (tx.vout[nvout] == MakeMarmaraCC1of2voutOpret(tx.vout[nvout].nValue, pk, opret)) - return true; - else - return false; -} - -// check if this is a LCL vout: -static bool vout_matches_createtxid_in_opret(const CTransaction &tx, int32_t nvout, const CScript &opret) +int64_t AddMarmarainputs(CMutableTransaction &mtx,std::vector &pubkeys,char *coinaddr,int64_t total,int32_t maxinputs) { - struct SMarmaraCreditLoopOpret loopData; - MarmaraDecodeLoopOpret(opret, loopData); - - CPubKey createtxidPk = CCtxidaddr_tweak(NULL, loopData.createtxid); - - if (tx.vout[nvout] == MakeMarmaraCC1of2voutOpret(tx.vout[nvout].nValue, createtxidPk, opret)) - return true; - else - return false; -} - - -// calls checker first for the cc vout opret then for the last vout opret -static bool get_either_opret(CMarmaraOpretCheckerBase *opretChecker, const CTransaction &tx, int32_t nvout, CScript &opretOut, CPubKey &opretpk) -{ - CScript opret; - bool isccopret = false, opretok = false; - - if (!opretChecker) - return false; - - // first check cc opret - if (GetCCOpReturnData(tx.vout[nvout].scriptPubKey, opret)) + uint64_t threshold,nValue,totalinputs = 0; uint256 txid,hashBlock; CTransaction tx; int32_t numvouts,ht,unlockht,vout,i,n = 0; uint8_t funcid; CPubKey pk; std::vector vals; + std::vector > unspentOutputs; + SetCCunspents(unspentOutputs,coinaddr,true); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "ccopret=" << opret.ToString() << std::endl); - if (opretChecker->CheckOpret(opret, opretpk)) + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( it->second.satoshis < threshold ) + continue; + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && vout < numvouts && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { - isccopret = true; - opretok = true; - opretOut = opret; - } - } - - // then check opret in the last vout: - if (!opretChecker->checkOnlyCC && !opretok) // if needed opret was not found in cc vout then check opret in the back of vouts - { - if (nvout < tx.vout.size()-1) { // there might be opret in the back - opret = tx.vout.back().scriptPubKey; - if (opretChecker->CheckOpret(opret, opretpk)) + if ( (funcid= DecodeMaramaraCoinbaseOpRet(tx.vout[numvouts-1].scriptPubKey,pk,ht,unlockht)) == 'C' || funcid == 'P' || funcid == 'L' ) { - isccopret = false; - opretok = true; - opretOut = opret; - } + //char str[64]; fprintf(stderr,"(%s) %s/v%d %.8f ht.%d unlockht.%d\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN,ht,unlockht); + if ( total != 0 && maxinputs != 0 ) + { + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + pubkeys.push_back(pk); + } + totalinputs += it->second.satoshis; + vals.push_back(it->second.satoshis); + n++; + if ( maxinputs != 0 && total == 0 ) + continue; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; + } else fprintf(stderr,"null funcid\n"); } } - - // print opret evalcode and funcid for debug logging: - vscript_t vprintopret; - uint8_t funcid = 0, evalcode = 0; - if (GetOpReturnData(opret, vprintopret) && vprintopret.size() >= 2) + if ( maxinputs != 0 && total == 0 ) { - evalcode = vprintopret.begin()[0]; - funcid = vprintopret.begin()[1]; + std::sort(vals.begin(),vals.end()); + totalinputs = 0; + for (i=0; i > unspentOutputs; + GetCCaddress1of2(cp,coinaddr,Marmarapk,mypk); + SetCCunspents(unspentOutputs,coinaddr,true); + threshold = remains / (MARMARA_VINS+1); + uint8_t mypriv[32]; + Myprivkey(mypriv); + CCaddr1of2set(cp,Marmarapk,mypk,mypriv,coinaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - // we allow activated coins funded from any normal inputs - // so this check is removed: - /* struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - - // if activated opret is okay - // check that vin txns have cc inputs (means they were checked by the pos or cc marmara validation code) - // this rule is disabled: `or tx is self-funded from normal inputs (marmaralock)` - // or tx is coinbase with activated opret - if (!tx_has_my_cc_vin(cp, tx) && TotalPubkeyNormalInputs(tx, pk_in_opret) == 0 && !tx.IsCoinBase()) + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( (nValue= it->second.satoshis) < threshold ) + continue; + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && vout < numvouts && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "vintx=" << tx.GetHash().GetHex() << " has no marmara cc inputs or self-funding normal inputs" << std::endl); - return false; - }*/ - - // vout is okay - return true; + if ( (funcid= DecodeMaramaraCoinbaseOpRet(tx.vout[numvouts-1].scriptPubKey,pk,ht,unlockht)) == 'C' || funcid == 'P' || funcid == 'L' ) + { + if ( unlockht < refunlockht ) + { + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + //fprintf(stderr,"merge CC vout %s/v%d %.8f unlockht.%d < ref.%d\n",txid.GetHex().c_str(),vout,(double)nValue/COIN,unlockht,refunlockht); + inputsum += nValue; + remains -= nValue; + if ( inputsum >= amount + txfee ) + { + //fprintf(stderr,"inputsum %.8f >= amount %.8f, update amount\n",(double)inputsum/COIN,(double)amount/COIN); + amount = inputsum - txfee; + break; + } + } + } + } + } + memset(mypriv,0,sizeof(mypriv)); + } + if ( inputsum >= amount+txfee ) + { + if ( inputsum > amount+txfee ) + { + change = (inputsum - amount); + mtx.vout.push_back(CTxOut(change,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); } + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,MarmaraCoinbaseOpret('L',height,mypk)); + if ( rawtx.size() == 0 ) + errorstr = (char *)"couldnt finalize CCtx"; else { - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "tx=" << tx.GetHash().GetHex() << " pubkey in opreturn does not match vout" << std::endl); - return false; + result.push_back(Pair("result",(char *)"success")); + result.push_back(Pair("hex",rawtx)); + return(result); } - } - return false; + } else errorstr = (char *)"insufficient funds"; + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",errorstr)); + return(result); } -/*bool IsMarmaraActivatedVout(const CTransaction &tx, int32_t nvout, CPubKey &pk_in_opret) +int32_t MarmaraSignature(uint8_t *utxosig,CMutableTransaction &mtx) { - CActivatedOpretChecker activatedOpretChecker; - CScript opret; - - if (nvout < 0 || nvout >= tx.vout.size()) - return false; - - // this check considers 2 cases: - // first if opret is in the cc vout data - // second if opret is in the last vout - if (CheckEitherOpRet(&activatedOpretChecker, tx, nvout, opret, pk_in_opret)) + uint256 txid,hashBlock; uint8_t *ptr; int32_t i,siglen,vout,numvouts; CTransaction tx; std::string rawtx; CPubKey mypk; std::vector pubkeys; struct CCcontract_info *cp,C; uint64_t txfee; + txfee = 10000; + vout = mtx.vin[0].prevout.n; + if ( myGetTransaction(mtx.vin[0].prevout.hash,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 && vout < numvouts ) { - // check opret pk matches vout - if (vout_matches_pk_in_opret(tx, nvout, opret)) + cp = CCinit(&C,EVAL_MARMARA); + mypk = pubkey2pk(Mypubkey()); + pubkeys.push_back(mypk); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,tx.vout[numvouts - 1].scriptPubKey,pubkeys); + if ( rawtx.size() > 0 ) { - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - - // if opret is okay - // check that vintxns have cc inputs - // or only self-funded from normals activated coins are allowed - for (auto const &vin : tx.vin) + siglen = mtx.vin[0].scriptSig.size(); + ptr = &mtx.vin[0].scriptSig[0]; + for (i=0; iismyvin(vin.scriptSig)) - { - CTransaction vintx; - uint256 hashBlock; - if (myGetTransaction(vin.prevout.hash, vintx, hashBlock) && !hashBlock.IsNull()) // not in mempool - { - CScript vintxOpret; - CPubKey pk_in_vintx_opret; - - if (CheckEitherOpRet(&activatedOpretChecker, vintx, vin.prevout.n, vintxOpret, pk_in_vintx_opret)) - { - if (tx_has_my_cc_vin(cp, vintx) || TotalPubkeyNormalInputs(vintx, pk_in_opret) > 0) - { - // check opret pk matches vintx vout - if (!vout_matches_pk_in_opret(tx, nvout, opret)) - { - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "vintx=" << vin.prevout.hash.GetHex() << " opreturn does not match vout (skipping this vout)" << std::endl); - return false; - } - } - else - { - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "vintx=" << vin.prevout.hash.GetHex() << " has no marmara cc inputs or not self-funded (skipping this vout)" << std::endl); - return false; - } - } - else - { - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "vintx=" << vin.prevout.hash.GetHex() << " has incorrect opreturn (skipping this vout)" << std::endl); - return false; - } - } - else - { - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "could not load vintx=" << vin.prevout.hash.GetHex() << " (vintx could be still in mempool)" << std::endl); - return false; - } - } - else - { - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "vins from other cc modules are not allowed, txid=" << tx.GetHash().GetHex() << " vin.nSequence=" << vin.nSequence << " (skipping this vout)" << std::endl); - return false; - } - } - } - } - else - { - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "tx=" << tx.GetHash().GetHex() << " pubkey in opreturn does not match vout (skipping this vout)" << std::endl); - return false; - } - } - return true; -} */ - - - -// checks if tx vout is valid locked-in-loop coins -// - activated opret is okay -// - vin txns are funded from marmara cc inputs (this means they were validated while added to the chain) -// returns the pubkey from the opret - -bool IsMarmaraLockedInLoopVout(const CTransaction &tx, int32_t nvout, CPubKey &pk_in_opret) -{ - CMarmaraLockInLoopOpretChecker lclOpretChecker; - CScript opret; - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - CPubKey Marmarapk = GetUnspendable(cp, NULL); - - if (nvout < 0 || nvout >= tx.vout.size()) - return false; - - // this check considers 2 cases: - // first if opret is in the cc vout data - // second if opret is in the last vout - if (get_either_opret(&lclOpretChecker, tx, nvout, opret, pk_in_opret)) - { - // check opret pk matches vout - if (vout_matches_createtxid_in_opret(tx, nvout, opret)) - { - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - - // if opret is okay - // check that vintxns have cc inputs - if (!tx_has_my_cc_vin(cp, tx)) - { - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "vintx=" << tx.GetHash().GetHex() << " has no marmara cc inputs" << std::endl); - return false; - } - // vout is okay - return true; - } - else - { - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "tx=" << tx.GetHash().GetHex() << " pubkey in opreturn does not match vout" << std::endl); - return false; - } - } - return false; -} - -// add activated or locked-in-loop coins from 1of2 address -// for lock-in-loop mypk not checked, so all locked-in-loop utxos for an address are added: -template -int64_t AddMarmaraCCInputs(IsMarmaraVoutT IsMarmaraVout, CMutableTransaction &mtx, std::vector &pubkeys, const char *unspentaddr, CAmount amount, int32_t maxinputs) -{ - CAmount totalinputs = 0, totaladded = 0; - - if (maxinputs > CC_MAXVINS) - maxinputs = CC_MAXVINS; - - // threshold not used any more - /*if (maxinputs > 0) - threshold = total / maxinputs; - else - threshold = total;*/ - - std::vector utxos; - std::vector > unspentOutputs; - SetCCunspents(unspentOutputs, (char*)unspentaddr, true); - - if (amount != 0 && unspentOutputs.size() > 0) // if amount == 0 only calc total - { - utxos.reserve(unspentOutputs.size()); - if (utxos.capacity() == 0) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "not enough memory to load utxos" << std::endl); - return -1; - } - } - - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "adding utxos from addr=" << unspentaddr << " total=" << amount << std::endl); - - // add all utxos from cc addr: - for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) - { - uint256 txid = it->first.txhash; - int32_t nvout = (int32_t)it->first.index; - uint256 hashBlock; - CTransaction tx; - - //TODO: decide on threshold usage, could be side effects like 'insufficient funds' error - //if (it->second.satoshis < threshold) - // continue; - - // check if vin might be already added to mtx: - if (std::find_if(mtx.vin.begin(), mtx.vin.end(), [&](CTxIn v) {return (v.prevout.hash == txid && v.prevout.n == nvout); }) != mtx.vin.end()) { - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "skipping already added txid=" << txid.GetHex() << " nvout=" << nvout << " satoshis=" << it->second.satoshis << std::endl); - continue; - } - - bool isSpentInMempool = false; - if (myGetTransaction(txid, tx, hashBlock) && - tx.vout.size() > 0 && - tx.vout[nvout].scriptPubKey.IsPayToCryptoCondition() && - !(isSpentInMempool = myIsutxo_spentinmempool(ignoretxid, ignorevin, txid, nvout))) - { - CPubKey opretpk; - //CScript opret; - std::vector< vscript_t > vParams; - //bool isccopret = false, opretok = false; - - // picks up either activated or LCL vouts - if (IsMarmaraVout(tx, nvout, opretpk)) //if (CheckEitherOpRet(opretChecker, tx, nvout, opret, senderpk)) - { - char utxoaddr[KOMODO_ADDRESS_BUFSIZE]; - - Getscriptaddress(utxoaddr, tx.vout[nvout].scriptPubKey); - if (strcmp(unspentaddr, utxoaddr) == 0) // check if the real vout address matches the index address (as another key could be used in the addressindex) - { - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "found good vintx for addr=" << unspentaddr << " txid=" << txid.GetHex() << " nvout=" << nvout << " satoshis=" << it->second.satoshis << std::endl); - - if (amount != 0) - { - CC_utxo ccutxo{ txid, it->second.satoshis, nvout }; - utxos.push_back(ccutxo); - pubkeys.push_back(opretpk); // add endorsers pubkeys - } - totalinputs += it->second.satoshis; - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "incorrect index addr=" << unspentaddr << " vs utxoaddr=" << utxoaddr << " txid=" << txid.GetHex() << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "addr=" << unspentaddr << " txid=" << txid.GetHex() << " nvout=" << nvout << " IsMarmaraVout returned false, skipping vout" << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "skipping txid=" << txid.GetHex() << " nvout=" << nvout << " satoshis=" << it->second.satoshis << " isSpentInMempool=" << isSpentInMempool << std::endl); - } - - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "for addr=" << unspentaddr << " found total=" << totalinputs << std::endl); - if (amount == 0) - return totalinputs; - - // add best selected utxos: - CAmount remains = amount; - while (utxos.size() > 0) - { - int64_t below = 0, above = 0; - int32_t abovei = -1, belowi = -1, ind = -1; - - if (CC_vinselect(&abovei, &above, &belowi, &below, utxos.data(), utxos.size(), remains) < 0) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "error finding unspent" << " remains=" << remains << " amount=" << amount << " utxos.size()=" << utxos.size() << std::endl); - return(0); - } - if (abovei >= 0) // best is 'above' - ind = abovei; - else if (belowi >= 0) // second try is 'below' - ind = belowi; - else - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "error finding unspent" << " remains=" << remains << " amount=" << amount << " abovei=" << abovei << " belowi=" << belowi << " ind=" << " utxos.size()=" << utxos.size() << ind << std::endl); - return(0); - } - - mtx.vin.push_back(CTxIn(utxos[ind].txid, utxos[ind].vout, CScript())); - totaladded += utxos[ind].nValue; - remains -= utxos[ind].nValue; - - // remove used utxo[ind]: - utxos[ind] = utxos.back(); - utxos.pop_back(); - - if (totaladded >= amount) // found the requested amount - break; - if (mtx.vin.size() >= maxinputs) // reached maxinputs - break; - } - -// if (totaladded < amount) // why do we need this? -// return 0; - - return totaladded; -} - - - -// finds the creation txid from the loop tx opret or -// return itself if it is the request tx -static int32_t get_create_txid(uint256 &createtxid, uint256 txid) -{ - CTransaction tx; - uint256 hashBlock; - - createtxid = zeroid; - if (myGetTransaction(txid, tx, hashBlock) != 0 && !hashBlock.IsNull() && tx.vout.size() > 1) // might be called from validation code, so non-locking version - { - uint8_t funcid; - struct SMarmaraCreditLoopOpret loopData; - - if ((funcid = MarmaraDecodeLoopOpret(tx.vout.back().scriptPubKey, loopData)) == MARMARA_ISSUE || funcid == MARMARA_TRANSFER || funcid == MARMARA_REQUEST ) { - createtxid = loopData.createtxid; - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "found for funcid=" << (char)funcid << " createtxid=" << createtxid.GetHex() << std::endl); - return(0); - } - else if (funcid == MARMARA_CREATELOOP) - { - if (createtxid == zeroid) // TODO: maybe this is not needed - createtxid = txid; - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "found for funcid=" << (char)funcid << " createtxid=" << createtxid.GetHex() << std::endl); - return(0); - } - } - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "could not get createtxid for txid=" << txid.GetHex() << " hashBlock.IsNull=" << hashBlock.IsNull() << " tx.vout.size()=" << tx.vout.size() << std::endl); - return(-1); -} - -// starting from any baton txid, finds the latest yet unspent batontxid -// adds createtxid MARMARA_CREATELOOP in creditloop vector (only if there are other txns in the loop) -// finds all the baton txids starting from the createtx (1+ in creditloop vector), apart from the latest baton txid -// returns the number of txns marked with the baton -int32_t MarmaraGetbatontxid(std::vector &creditloop, uint256 &batontxid, uint256 querytxid) -{ - uint256 createtxid; - int64_t value; - int32_t vini, height, n = 0; - const int32_t NO_MEMPOOL = 0; - const int32_t DO_LOCK = 1; - - uint256 txid = querytxid; - batontxid = zeroid; - if (get_create_txid(createtxid, txid) == 0) // retrieve the initial creation txid - { - uint256 spenttxid; - txid = createtxid; - //fprintf(stderr,"%s txid.%s -> createtxid %s\n", logFuncName, txid.GetHex().c_str(),createtxid.GetHex().c_str()); - - while (CCgetspenttxid(spenttxid, vini, height, txid, MARMARA_BATON_VOUT) == 0) // while the current baton is spent - { - creditloop.push_back(txid); - //fprintf(stderr,"%d: %s\n",n,txid.GetHex().c_str()); - n++; - if ((value = CCgettxout(spenttxid, MARMARA_BATON_VOUT, NO_MEMPOOL, DO_LOCK)) == 10000) //check if the baton value is unspent yet - this is the last baton - { - batontxid = spenttxid; - //fprintf(stderr,"%s got baton %s %.8f\n", logFuncName, batontxid.GetHex().c_str(),(double)value/COIN); - return n; - } - else if (value > 0) - { - batontxid = spenttxid; - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "n=" << n << " found and will use false baton=" << batontxid.GetHex() << " vout=" << MARMARA_BATON_VOUT << " value=" << value << std::endl); - return n; - } - // TODO: get funcid (and check?) - txid = spenttxid; - } - - if (n == 0) - return 0; // empty loop - else - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "n != 0 return bad loop querytxid=" << querytxid.GetHex() << " n=" << n << std::endl); - return -1; //bad loop - } - } - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "could not get createtxid for querytxid=" << querytxid.GetHex() << std::endl); - return -1; -} - -static int32_t get_settlement_txid(uint256 &settletxid, uint256 issuetxid) -{ - int32_t vini, height; - - if (CCgetspenttxid(settletxid, vini, height, issuetxid, MARMARA_OPENCLOSE_VOUT) == 0) // NOTE: CCgetspenttxid checks also mempool - { - return 0; - } - return -1; -} - -// load the create tx and adds data from its opret to loopData safely, with no overriding -static int32_t get_loop_creation_data(uint256 createtxid, struct SMarmaraCreditLoopOpret &loopData) -{ - CTransaction tx; - uint256 hashBlock; - - if (myGetTransaction(createtxid, tx, hashBlock) != 0 && !hashBlock.IsNull() && tx.vout.size() > 1) // might be called from validation code, so non-locking version - { - uint8_t funcid; - vscript_t vopret; - - // first check if this is really createtx to prevent override loopData with other tx type data: - if (GetOpReturnData(tx.vout.back().scriptPubKey, vopret) && vopret.size() >= 2 && vopret.begin()[0] == EVAL_MARMARA && vopret.begin()[1] == MARMARA_CREATELOOP) - { - if ((funcid = MarmaraDecodeLoopOpret(tx.vout.back().scriptPubKey, loopData)) == MARMARA_CREATELOOP) { - return(0); //0 is okay - } - } - } - return(-1); -} - -// consensus code: - -// check total loop amount in tx and redistributed back amount: -static bool check_lcl_redistribution(const CTransaction &tx, uint256 prevtxid, int32_t startvin, std::string &errorStr) -{ - std::vector creditloop; - uint256 batontxid, createtxid; - struct SMarmaraCreditLoopOpret creationLoopData; - struct SMarmaraCreditLoopOpret currentLoopData; - int32_t nPrevEndorsers = 0; - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "checking prevtxid=" << prevtxid.GetHex() << std::endl); - - if ((nPrevEndorsers = MarmaraGetbatontxid(creditloop, batontxid, prevtxid)) < 0) { // number of endorsers + issuer, without the current tx - errorStr = "could not get credit loop"; - return false; - } - - createtxid = creditloop.empty() ? prevtxid : creditloop[0]; - if (get_loop_creation_data(createtxid, creationLoopData) < 0) - { - errorStr = "could not get credit loop creation data"; - return false; - } - - // get opret data - if (tx.vout.size() == 0 || MarmaraDecodeLoopOpret(tx.vout.back().scriptPubKey, currentLoopData) == 0) - { - errorStr = "no opreturn found in the last vout of issue/transfer tx "; - return false; - } - - // check loop endorsers are funded correctly: - CAmount lclAmount = 0L; - std::list endorserPks; - for (int32_t i = 0; i < tx.vout.size() - 1; i ++) // except the last vout opret - { - if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition()) - { - CScript opret; - SMarmaraCreditLoopOpret voutLoopData; - - if (GetCCOpReturnData(tx.vout[i].scriptPubKey, opret) && MarmaraDecodeLoopOpret(opret, voutLoopData) == MARMARA_LOCKED) - { - CPubKey createtxidPk = CCtxidaddr_tweak(NULL, createtxid); - if (tx.vout[i] != MakeMarmaraCC1of2voutOpret(tx.vout[i].nValue, createtxidPk, opret)) - { - errorStr = "MARMARA_LOCKED cc output incorrect: pubkey does not match"; - return false; - } - - // check each vout is 1/N lcl amount - CAmount diff = tx.vout[i].nValue != creationLoopData.amount / (nPrevEndorsers + 1); - if (diff < -MARMARA_LOOP_TOLERANCE || diff > MARMARA_LOOP_TOLERANCE) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "normal output amount incorrect: i=" << i << " tx.vout[i].nValue=" << tx.vout[i].nValue << " creationLoopData.amount=" << creationLoopData.amount << " nPrevEndorsers=" << nPrevEndorsers << " creationLoopData.amount / (nPrevEndorsers + 1)=" << (creationLoopData.amount / (nPrevEndorsers + 1)) << std::endl); - errorStr = "MARMARA_LOCKED cc output amount incorrect"; - return false; - } - - - lclAmount += tx.vout[i].nValue; - endorserPks.push_back(voutLoopData.pk); - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "vout pubkey=" << HexStr(vuint8_t(voutLoopData.pk.begin(), voutLoopData.pk.end())) << " vout[i].nValue=" << tx.vout[i].nValue << std::endl); - } - /* for issue tx no MARMARA_LOCKED vouts: - else - { - errorStr = "no MARMARA_LOCKED funcid found in cc opreturn"; - return false; - } */ - } - } - - // check loop amount: - if (creationLoopData.amount != lclAmount) - { - errorStr = "tx LCL amount invalid"; - return false; - } - - // the latest endorser does not receive back to normal - CPubKey latestpk = endorserPks.front(); - endorserPks.pop_front(); - - if (nPrevEndorsers != endorserPks.size()) // now endorserPks is without the current endorser - { - errorStr = "incorrect number of endorsers pubkeys found in tx"; - return false; - } - - if (nPrevEndorsers != 0) - { - // calc total redistributed amount to endorsers' normal outputs: - CAmount redistributedAmount = 0L; - for (const auto &v : tx.vout) - { - if (!v.scriptPubKey.IsPayToCryptoCondition()) - { - // check if a normal matches to any endorser pubkey - for (const auto & pk : endorserPks) - { - if (v == CTxOut(v.nValue, CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG)) - { - CAmount diff = v.nValue - creationLoopData.amount / (nPrevEndorsers + 1); - if (diff < -MARMARA_LOOP_TOLERANCE || diff > MARMARA_LOOP_TOLERANCE) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "normal output amount incorrect: v.nValue=" << v.nValue << " creationLoopData.amount=" << creationLoopData.amount << " nPrevEndorsers=" << nPrevEndorsers << " creationLoopData.amount / (nPrevEndorsers + 1)=" << (creationLoopData.amount / (nPrevEndorsers + 1)) << std::endl); - errorStr = "normal output amount incorrect"; - return false; - } - redistributedAmount += v.nValue; - } - } - } - } - // only one new endorser should remain without back payment to a normal output - /*if (endorserPks.size() != 1) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "invalid redistribution to normals: left endorserPks.size()=" << endorserPks.size() << std::endl); - errorStr = "tx redistribution amount to normals invalid"; - return false; - }*/ - - // check that 'redistributed amount' == (N-1)/N * 'loop amount' (nPrevEndorsers == N-1) - CAmount diff = lclAmount - lclAmount / (nPrevEndorsers + 1) - redistributedAmount; - if (diff < -MARMARA_LOOP_TOLERANCE || diff > MARMARA_LOOP_TOLERANCE) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "invalid redistribution to normal outputs: lclAmount=" << lclAmount << " redistributedAmount =" << redistributedAmount << " nPrevEndorsers=" << nPrevEndorsers << " lclAmount / (nPrevEndorsers+1)=" << (lclAmount / (nPrevEndorsers + 1)) << std::endl); - errorStr = "invalid redistribution to normal outputs"; - return false; - } - } - - // enum spent locked-in-loop vins and collect pubkeys - std::set endorserPksPrev; - for (int32_t i = startvin; i >= 0 && i < tx.vin.size(); i++) - { - if (IsCCInput(tx.vin[i].scriptSig)) - { - if (cp->ismyvin(tx.vin[i].scriptSig)) - { - CTransaction vintx; - uint256 hashBlock; - - if (myGetTransaction(tx.vin[i].prevout.hash, vintx, hashBlock) /*&& !hashBlock.IsNull()*/) - { - CPubKey pk_in_opret; - if (IsMarmaraLockedInLoopVout(vintx, tx.vin[i].prevout.n, pk_in_opret)) // if vin not added by AddMarmaraCCInputs - { - endorserPksPrev.insert(pk_in_opret); - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "vintx pubkey=" << HexStr(vuint8_t(pk_in_opret.begin(), pk_in_opret.end())) << std::endl); - } - else - { - errorStr = "issue/transfer tx has unexpected non-lcl marmara cc vin"; - return false; - } - } - else - { - errorStr = "issue/transfer tx: can't get vintx for vin=" + std::to_string(i); - return false; - } - } - else - { - errorStr = "issue/transfer tx cannot have non-marmara cc vins"; - return false; - } - } - } - - // convert to set to compare - std::set endorserPksSet(endorserPks.begin(), endorserPks.end()); - if (endorserPksSet != endorserPksPrev) - { - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "LCL vintx pubkeys do not match vout pubkeys" << std::endl); - for (const auto &pk : endorserPksPrev) - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "vintx pubkey=" << HexStr(vuint8_t(pk.begin(), pk.end())) << std::endl); - for (const auto &pk : endorserPksSet) - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "vout pubkey=" << HexStr(vuint8_t(pk.begin(), pk.end())) << std::endl); - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "popped vout last pubkey=" << HexStr(vuint8_t(latestpk.begin(), latestpk.end())) << std::endl); - errorStr = "issue/transfer tx has incorrect loop pubkeys"; - return false; - } - return true; -} - -// check request or create tx -static bool check_request_tx(uint256 requesttxid, CPubKey receiverpk, uint8_t issueFuncId, std::string &errorStr) -{ - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - CPubKey Marmarapk = GetUnspendable(cp, NULL); - - // make sure less than maxlength (?) - - uint256 createtxid; - struct SMarmaraCreditLoopOpret loopData; - CTransaction requesttx; - uint256 hashBlock; - uint8_t funcid = 0; - - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "checking requesttxid=" << requesttxid.GetHex() << std::endl); - - if (requesttxid.IsNull()) - errorStr = "requesttxid can't be empty"; - else if (get_create_txid(createtxid, requesttxid) < 0) - errorStr = "can't get createtxid from requesttxid (request tx could be in mempool)"; - // check requested cheque params: - else if (get_loop_creation_data(createtxid, loopData) < 0) - errorStr = "cannot get loop creation data"; - else if (!myGetTransaction(requesttxid, requesttx, hashBlock)) - errorStr = "cannot get request transaction"; - // TODO: do we need here to check the request tx in mempool? - else if (hashBlock.IsNull()) /*is in mempool?*/ - errorStr = "request transaction still in mempool"; - else if (requesttx.vout.size() < 1 || (funcid = MarmaraDecodeLoopOpret(requesttx.vout.back().scriptPubKey, loopData)) == 0) - errorStr = "cannot decode request tx opreturn data"; - else if (TotalPubkeyNormalInputs(requesttx, receiverpk) == 0) // extract and check the receiver pubkey - errorStr = "receiver pubkey does not match signer of request tx"; - else if (TotalPubkeyNormalInputs(requesttx, loopData.pk) > 0) // extract and check the receiver pubkey - errorStr = "sender pk signed request tx, cannot request credit from self"; - else if (loopData.matures <= chainActive.LastTip()->GetHeight()) - errorStr = "credit loop must mature in the future"; - - else { - if (issueFuncId == MARMARA_ISSUE && funcid != MARMARA_CREATELOOP) - errorStr = "not a create tx"; - if (issueFuncId == MARMARA_TRANSFER && funcid != MARMARA_REQUEST) - errorStr = "not a request tx"; - } - - if (!errorStr.empty()) - return false; - else - return true; -} - -// check issue or transfer tx -static bool check_issue_tx(const CTransaction &tx, std::string &errorStr) -{ - struct SMarmaraCreditLoopOpret loopData; - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - - if (tx.vout.size() == 0) { - errorStr = "bad issue or transfer tx: no vouts"; - return false; - } - - MarmaraDecodeLoopOpret(tx.vout.back().scriptPubKey, loopData); - if (loopData.lastfuncid != MARMARA_ISSUE && loopData.lastfuncid != MARMARA_TRANSFER) { - errorStr = "not an issue or transfer tx"; - return false; - } - - CPubKey marmarapk = GetUnspendable(cp, NULL); - - // check activated vouts - std::list nbatonvins; - bool activatedHasBegun = false; - int i = 0; - for (; i < tx.vin.size(); i ++) - { - if (IsCCInput(tx.vin[i].scriptSig)) - { - if (cp->ismyvin(tx.vin[i].scriptSig)) - { - CTransaction vintx; - uint256 hashBlock; - - if (myGetTransaction(tx.vin[i].prevout.hash, vintx, hashBlock) /*&& !hashBlock.IsNull()*/) - { - CPubKey pk_in_opret; - if (IsMarmaraActivatedVout(vintx, tx.vin[i].prevout.n, pk_in_opret)) // if vin not added by AddMarmaraCCInputs - { - if (check_signing_pubkey(tx.vin[i].scriptSig) == marmarapk) - { - // disallow spending with marmara global privkey: - errorStr = "cannot spend activated coins using marmara global pubkey"; - return false; - } - activatedHasBegun = true; - } - else - { - // nbatonvins.push_back(i); // this is probably baton or request tx - if (activatedHasBegun) - break; // activated vouts ended, break - } - } - else - { - errorStr = "issue/transfer tx: can't get vintx for vin=" + std::to_string(i); - return false; - } - } - else - { - errorStr = "issue/transfer tx cannot have non-marmara cc vins"; - return false; - } - } - } - - // stop at find request tx, it is in the first cc input after added activated cc inputs: - - // if (nbatonvins.size() == 0) - if (i >= tx.vin.size()) - { - errorStr = "invalid issue/transfer tx: no request tx vin"; - return false; - } - //int32_t requesttx_i = nbatonvins.front(); - int32_t requesttx_i = i; - //nbatonvins.pop_front(); - - if (!check_request_tx(tx.vin[requesttx_i].prevout.hash, loopData.pk, loopData.lastfuncid, errorStr)) - return false; - - // prev tx is either creation tx or baton tx (and not a request tx for MARMARA_TRANSFER) - uint256 prevtxid; - if (loopData.lastfuncid == MARMARA_ISSUE) - prevtxid = tx.vin[requesttx_i].prevout.hash; - - if (loopData.lastfuncid == MARMARA_TRANSFER) - { - CTransaction vintx; - uint256 hashBlock; - - //if (nbatonvins.size() == 0) - if (++i >= tx.vin.size()) - { - errorStr = "no baton vin in transfer tx"; - return false; - } - int32_t baton_i = i; - //baton_i = nbatonvins.front(); - //nbatonvins.pop_front(); - - // TODO: check that the baton tx is a cc tx: - if (myGetTransaction(tx.vin[baton_i].prevout.hash, vintx, hashBlock) /*&& !hashBlock.IsNull()*/) - { - if (!tx_has_my_cc_vin(cp, vintx)) { - errorStr = "no marmara cc vins in baton tx for transfer tx"; - return false; - } - } - prevtxid = tx.vin[baton_i].prevout.hash; - } - - //if (nbatonvins.size() != 0) // no other vins should present - //{ - // errorStr = "unknown cc vin(s) in issue/transfer tx"; - // return false; - //} - - - //if (loopData.lastfuncid == MARMARA_TRANSFER) // maybe for issue tx it could work too - //{ - // check LCL fund redistribution and vouts in transfer tx - i++; - if (!check_lcl_redistribution(tx, prevtxid, i, errorStr)) - return false; - //} - - // check issue tx vouts... - // ...checked in check_lcl_redistribution - - return true; -} - - -static bool check_settlement_tx(const CTransaction &settletx, std::string &errorStr) -{ - std::vector creditloop; - uint256 batontxid, createtxid; - struct SMarmaraCreditLoopOpret creationLoopData; - struct SMarmaraCreditLoopOpret currentLoopData; - struct SMarmaraCreditLoopOpret batonLoopData; - int32_t nPrevEndorsers = 0; - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - - // check settlement tx has vins and vouts - if (settletx.vout.size() == 0) { - errorStr = "bad settlement tx: no vouts"; - return false; - } - - if (settletx.vin.size() == 0) { - errorStr = "bad settlement tx: no vins"; - return false; - } - - // check settlement tx funcid - MarmaraDecodeLoopOpret(settletx.vout.back().scriptPubKey, currentLoopData); - if (currentLoopData.lastfuncid != MARMARA_SETTLE && currentLoopData.lastfuncid != MARMARA_SETTLE_PARTIAL) { - errorStr = "not a settlement tx"; - return false; - } - - // check settlement tx spends correct open-close baton - if (settletx.vin[0].prevout.n != MARMARA_OPENCLOSE_VOUT) { - errorStr = "incorrect settlement tx vin0"; - return false; - } - - // check issue tx referred by settlement tx - uint256 issuetxid = settletx.vin[0].prevout.hash; - CTransaction issuetx; - uint256 hashBlock; - if (!myGetTransaction(issuetxid, issuetx, hashBlock) /*&& !hashBlock.IsNull()*/) - { - errorStr = "could not load issue tx"; - return false; - } - if (check_issue_tx(issuetx, errorStr)) { - return false; - } - - // get baton txid and creaditloop - if (MarmaraGetbatontxid(creditloop, batontxid, issuetxid) <= 0 || creditloop.empty()) { // returns number of endorsers + issuer - errorStr = "could not get credit loop or no endorsers"; - return false; - } - - // get credit loop basic data (loop amount) - createtxid = creditloop[0]; - if (get_loop_creation_data(createtxid, creationLoopData) < 0) - { - errorStr = "could not get credit loop creation data"; - return false; - } - - // check mature height: - if (chainActive.LastTip()->GetHeight() < creationLoopData.matures) - { - errorStr = "credit loop does not mature yet"; - return false; - } - // get current baton tx - CTransaction batontx; - if (!myGetTransaction(batontxid, batontx, hashBlock) /*&& !hashBlock.IsNull()*/) - { - errorStr = "could not load baton tx"; - return false; - } - if (batontx.vout.size() == 0) { - errorStr = "bad baton tx: no vouts"; - return false; - } - // get baton tx opret (we need holder pk from there) - MarmaraDecodeLoopOpret(batontx.vout.back().scriptPubKey, batonLoopData); - if (batonLoopData.lastfuncid != MARMARA_ISSUE && batonLoopData.lastfuncid != MARMARA_TRANSFER) { - errorStr = "baton tx not a issue or transfer tx"; - return false; - } - -/* - // get endorser pubkeys - CAmount lclAmount = 0L; - std::list endorserPks; - // find request tx, it is in the first cc input after added activated cc inputs: - for (int i = 0; i < tx.vin.size() - 1; i++) - { - if (IsCCInput(tx.vin[i].scriptSig)) - { - if (cp->ismyvin(tx.vin[i].scriptSig)) - { - CTransaction vintx; - uint256 hashBlock; - - if (myGetTransaction(tx.vin[i].prevout.hash, vintx, hashBlock) /*&& !hashBlock.IsNull()*//*) - { - CPubKey pk_in_opret; - if (IsMarmaraLockedInLoopVout(vintx, tx.vin[i].prevout.n, pk_in_opret)) // if vin added by AddMarmaraCCInputs - { - endorserPks.push_back(pk_in_opret); - lclAmount += vintx.vout[tx.vin[i].prevout.n].nValue; - } - } - else - { - errorStr = "settlement tx: can't get vintx for vin=" + std::to_string(i); - return false; - } - } - else - { - errorStr = "settlement tx cannot have non-marmara cc vins"; - return false; - } - } - } -*/ - - //find settled amount to the holder - CAmount settledAmount = 0L; - for (const auto &v : settletx.vout) // except the last vout opret - { - if (!v.scriptPubKey.IsPayToCryptoCondition()) - { - if (v == CTxOut(v.nValue, CScript() << ParseHex(HexStr(batonLoopData.pk)) << OP_CHECKSIG)) - { - settledAmount += v.nValue; - } - } - else - { - // do not allow any cc vouts - // NOTE: what about if change occures in settlement because someone has sent some coins to the loop? - // such coins should be either skipped by IsMarmaraLockedInLoopVout, because they dont have cc inputs - // or such cc transactions will be rejected as invalid - errorStr = "settlement tx cannot have unknown cc vouts"; - return false; - } - } - - // check settled amount equal to loop amount - CAmount diff = creationLoopData.amount - settledAmount; - if (currentLoopData.lastfuncid == MARMARA_SETTLE && !(diff <= 0)) - { - errorStr = "payment to holder incorrect for full settlement"; - return false; - } - // check settled amount less than loop amount for partial settlement - if (currentLoopData.lastfuncid == MARMARA_SETTLE_PARTIAL && !(diff > 0)) - { - errorStr = "payment to holder incorrect for partial settlement"; - return false; - } - return true; -} - - - -//#define HAS_FUNCID(v, funcid) (std::find((v).begin(), (v).end(), funcid) != (v).end()) -#define FUNCID_SET_TO_STRING(funcids) [](const std::set &s) { std::string r; for (auto const &e : s) r += e; return r; }(funcids) - -bool MarmaraValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) -{ - if (!ASSETCHAINS_MARMARA) - return eval->Invalid("-ac_marmara must be set for marmara CC"); - - if (tx.vout.size() < 1) - return eval->Invalid("no vouts"); - - CPubKey Marmarapk = GetUnspendable(cp, 0); - std::string validationError; - std::set funcIds; - - for (int32_t i = 0; i < tx.vout.size(); i++) - { - CPubKey opretpk; - CScript opret; - CMarmaraActivatedOpretChecker activatedChecker; - CMarmaraLockInLoopOpretChecker lockinloopChecker; - - // temp simple check for opret presence - if (get_either_opret(&activatedChecker, tx, i, opret, opretpk)) - { - CPubKey pk; - int32_t h, uh; - uint8_t funcid = MarmaraDecodeCoinbaseOpret(opret, pk, h, uh); - funcIds.insert(funcid); - } - else if (get_either_opret(&lockinloopChecker, tx, i, opret, opretpk)) - { - struct SMarmaraCreditLoopOpret loopData; - MarmaraDecodeLoopOpret(opret, loopData); - funcIds.insert(loopData.lastfuncid); - } - } - - if (funcIds.empty()) - return eval->Invalid("invalid or no opreturns"); - - if (funcIds == std::set{MARMARA_POOL}) - { - int32_t ht, unlockht, vht, vunlockht; - CPubKey pk, vpk; - uint8_t funcid = MarmaraDecodeCoinbaseOpret(tx.vout.back().scriptPubKey, pk, ht, unlockht); - - for (int32_t i = 0; i < tx.vin.size(); i++) - { - if ((*cp->ismyvin)(tx.vin[i].scriptSig) != 0) - { - CTransaction vinTx; - uint256 hashBlock; - - if (eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) - return eval->Invalid("cant find vinTx"); - else - { - if (vinTx.IsCoinBase() == 0) - return eval->Invalid("noncoinbase input"); - else if (vinTx.vout.size() != 2) - return eval->Invalid("coinbase doesnt have 2 vouts"); - uint8_t vfuncid = MarmaraDecodeCoinbaseOpret(vinTx.vout[1].scriptPubKey, vpk, vht, vunlockht); - if (vfuncid != MARMARA_COINBASE || vpk != pk || vunlockht != unlockht) - return eval->Invalid("mismatched opreturn"); - } - } - } - return(true); - } - else if (funcIds == std::set{MARMARA_LOOP}) // locked in loop funds - { - // TODO: check this, seems error() is better than invalid(): - return eval->Error("unexpected tx funcid MARMARA_LOOP"); // this tx should have no cc inputs - } - else if (funcIds == std::set{MARMARA_CREATELOOP}) // create credit loop - { - return eval->Error("unexpected tx funcid MARMARA_CREATELOOP"); // this tx should have no cc inputs - } - else if (funcIds == std::set{MARMARA_REQUEST}) // receive -> agree to receive MARMARA_ISSUE from pk, amount, currency, due ht - { - return eval->Error("unexpected tx funcid MARMARA_REQUEST"); // tx should have no cc inputs - } - else if (funcIds == std::set{MARMARA_ISSUE} || funcIds == std::set{MARMARA_ISSUE, MARMARA_LOCKED} || funcIds == std::set{MARMARA_ACTIVATED, MARMARA_ISSUE, MARMARA_LOCKED}) // issue -> issue currency to pk with due mature height - { - if (!check_issue_tx(tx, validationError)) - return eval->Error(validationError); // tx have no cc inputs - else - return true; - } - else if (funcIds == std::set{MARMARA_TRANSFER} || funcIds == std::set{MARMARA_TRANSFER, MARMARA_LOCKED} || funcIds == std::set{MARMARA_ACTIVATED, MARMARA_TRANSFER, MARMARA_LOCKED}) // transfer -> given MARMARA_REQUEST transfer MARMARA_ISSUE or MARMARA_TRANSFER to the pk of MARMARA_REQUEST - { - if (!check_issue_tx(tx, validationError)) - return eval->Error(validationError); // tx have no cc inputs - else - return true; - } - else if (funcIds == std::set{MARMARA_SETTLE}) // settlement -> automatically spend issuers locked funds, given MARMARA_ISSUE - { - if (!check_settlement_tx(tx, validationError)) - return eval->Error(validationError); // tx have no cc inputs - else - return true; - } - else if (funcIds == std::set{MARMARA_SETTLE_PARTIAL}) // insufficient settlement - { - if (!check_settlement_tx(tx, validationError)) - return eval->Error(validationError); // tx have no cc inputs - else - return true; - } - else if (funcIds == std::set{MARMARA_COINBASE} /*|| funcIds == std::set{MARMARA_ACTIVATED_3X }*/) // coinbase - { - return true; - } - else if (funcIds == std::set{MARMARA_LOCKED}) // pk in lock-in-loop - { - return true; // will be checked in PoS validation code - } - else if (funcIds == std::set{MARMARA_ACTIVATED} || funcIds == std::set{MARMARA_COINBASE_3X}) // activated - { - return true; // will be checked in PoS validation code - } - else if (funcIds == std::set{MARMARA_RELEASE}) // released to normal - { - return(true); // TODO: decide if deactivation is allowed - } - // staking only for locked utxo - - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << " validation error for txid=" << tx.GetHash().GetHex() << " tx has bad funcids=" << FUNCID_SET_TO_STRING(funcIds) << std::endl); - return eval->Invalid("fall through error"); -} -// end of consensus code - - - -// set marmara coinbase opret for even blocks -// this is also activated coins opret -CScript MarmaraCoinbaseOpret(uint8_t funcid, int32_t height, CPubKey pk) -{ - uint8_t *ptr; - //fprintf(stderr,"height.%d pksize.%d\n",height,(int32_t)pk.size()); - if (height > 0 && (height & 1) == 0 && pk.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) - return(EncodeMarmaraCoinbaseOpRet(funcid, pk, height)); - else - return(CScript()); -} - -// returns coinbase scriptPubKey with 1of2 addr where coins will go in createNewBlock in miner.cpp -// also adds cc opret -CScript MarmaraCreateDefaultCoinbaseScriptPubKey(int32_t nHeight, CPubKey minerpk) -{ - //std::cerr << __func__ << " nHeight=" << nHeight << std::endl; - if (nHeight > 0 && (nHeight & 1) == 0) - { - char coinaddr[KOMODO_ADDRESS_BUFSIZE]; - CScript opret = MarmaraCoinbaseOpret(MARMARA_COINBASE, nHeight, minerpk); - CTxOut ccvout; - - if (minerpk.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "bad minerpk=" << HexStr(minerpk) << std::endl); - return CScript(); - } - - // use special rewards pubkey for testing - //std::string marmara_test_pubkey_param = GetArg("-marmara-test-pubkey", ""); - //if (!marmara_test_pubkey_param.empty()) { - // minerpk = pubkey2pk(ParseHex(marmara_test_pubkey_param)); - //} - - // set initial amount to zero, it will be overriden by miner's code - ccvout = MakeMarmaraCC1of2voutOpret(0, minerpk, opret); // add cc opret to coinbase - //Getscriptaddress(coinaddr, ccvout.scriptPubKey); - //LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "for activated rewards using pk=" << HexStr(minerpk) << " height=" << nHeight << " 1of2addr=" << coinaddr << std::endl); - //std::cerr << __func__ << " created activated opret for h=" << nHeight << std::endl; - return(ccvout.scriptPubKey); - } - else - { - //LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "not even ht, returning normal scriptPubKey" << std::endl); - //std::cerr << __func__ << " created normal opret for h=" << nHeight << std::endl; - return CScript() << ParseHex(HexStr(minerpk)) << OP_CHECKSIG; - } -} - -// creates coinbase transaction: adds marmara opreturn -// -- now actually does nothing as opret is already in the cc vout - see Marmara_scriptPubKey() -// ++ now creates coinbase vout depending on staketx -CScript MarmaraCreatePoSCoinbaseScriptPubKey(int32_t nHeight, const CScript &defaultspk, const CTransaction &staketx) -{ - CScript spk = defaultspk; - - if (nHeight > 0 && (nHeight & 1) == 0) - { - if (staketx.vout.size() > 0) - { - char checkaddr[KOMODO_ADDRESS_BUFSIZE]; - CScript opret; - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - CPubKey Marmarapk = GetUnspendable(cp, 0); - CPubKey opretpk; - - // for stake tx check only cc opret, in last-vout opret there is pos data: - CMarmaraActivatedOpretChecker activatedChecker; - CMarmaraLockInLoopOpretChecker lockinloopChecker(CHECK_ONLY_CCOPRET); - - if (get_either_opret(&activatedChecker, staketx, 0, opret, opretpk)) - { - CScript coinbaseOpret = MarmaraCoinbaseOpret(MARMARA_COINBASE, nHeight, opretpk); - CTxOut vout = MakeMarmaraCC1of2voutOpret(0, opretpk, coinbaseOpret); - - Getscriptaddress(checkaddr, vout.scriptPubKey); - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "created activated coinbase scriptPubKey with address=" << checkaddr << std::endl); - spk = vout.scriptPubKey; - } - else if (get_either_opret(&lockinloopChecker, staketx, 0, opret, opretpk)) - { - CScript coinbaseOpret = MarmaraCoinbaseOpret(MARMARA_COINBASE_3X, nHeight, opretpk); - CTxOut vout = MakeMarmaraCC1of2voutOpret(0, opretpk, coinbaseOpret); - - Getscriptaddress(checkaddr, vout.scriptPubKey); - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "created activated 3x coinbase scriptPubKey address=" << checkaddr << std::endl); - spk = vout.scriptPubKey; - } - else - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "cannot create pos marmara coinbase scriptPubKey, could not decode stake tx cc opret:" << staketx.vout[0].scriptPubKey.ToString() << std::endl); - } - } - else - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "cannot create pos marmara coinbase scriptPubKey, bad staketx:" << " staketx.vout.size()=" << staketx.vout.size() << std::endl); - } - - } - // else: use default coinbase for odd heights - return spk; -} - -// half of the blocks (with even heights) should be mined as activated (to some unlock height) -// validates opreturn for even blocks -int32_t MarmaraValidateCoinbase(int32_t height, CTransaction tx, std::string &errmsg) -{ -/* if (0) // not used - { - int32_t d, histo[365 * 2 + 30]; - memset(histo, 0, sizeof(histo)); - for (ht = 2; ht < 100; ht++) - fprintf(stderr, "%d ", MarmaraUnlockht(ht)); - fprintf(stderr, " <- first 100 unlock heights\n"); - for (ht = 2; ht < 1000000; ht += MARMARA_GROUPSIZE) - { - d = (MarmaraUnlockht(ht) - ht) / 1440; - if (d < 0 || d > sizeof(histo) / sizeof(*histo)) - fprintf(stderr, "d error.%d at ht.%d\n", d, ht); - else histo[d]++; - } - - std::cerr << " "; - for (ht = 0; ht < sizeof(histo) / sizeof(*histo); ht++) - fprintf(stderr, "%d ", histo[ht]); - fprintf(stderr, "<- unlock histogram[%d] by days locked\n", (int32_t)(sizeof(histo) / sizeof(*histo))); - } */ - - if ((height & 1) != 0) // odd block - no marmara opret - { - return(0); // TODO: do we need to check here that really no marmara coinbase opret for odd blocks? - } - else //even block - check for cc vout & opret - { - int32_t ht, unlockht; - CTxOut ccvout; - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - CPubKey Marmarapk = GetUnspendable(cp, NULL); - - if (tx.vout.size() >= 1 && tx.vout.size() <= 2) // NOTE: both cc and last vout oprets are supported in coinbases - { - CScript opret; - CPubKey dummypk, opretpk; - CMarmaraActivatedOpretChecker activatedChecker; - - //vuint8_t d(tx.vout[0].scriptPubKey.begin(), tx.vout[0].scriptPubKey.end()); - //std::cerr << __func__ << " vtx cc opret=" << HexStr(d) << " height=" << height << std::endl; - if (!get_either_opret(&activatedChecker, tx, 0, opret, dummypk)) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "can't find coinbase opret (this could happen on multiproc computers sometimes)" << std::endl); - errmsg = "marmara cc bad coinbase opreturn (this is normal on multiproc computers if happens only sometimes)"; - return -1; - } - - if (IsFuncidOneOf( MarmaraDecodeCoinbaseOpret(opret, opretpk, ht, unlockht), { MARMARA_COINBASE, MARMARA_COINBASE_3X } )) - { - if (ht == height && MarmaraUnlockht(height) == unlockht) - { - std::vector< vscript_t > vParams; - CScript ccvoutCoinbase; - - ccvout = MakeCC1of2vout(EVAL_MARMARA, 0, Marmarapk, opretpk); // TODO: check again if pk matches the address - tx.vout[0].scriptPubKey.IsPayToCryptoCondition(&ccvoutCoinbase, vParams); - if (ccvout.scriptPubKey == ccvoutCoinbase) - return(0); - - char addr0[KOMODO_ADDRESS_BUFSIZE], addr1[KOMODO_ADDRESS_BUFSIZE]; - Getscriptaddress(addr0, ccvout.scriptPubKey); - Getscriptaddress(addr1, tx.vout[0].scriptPubKey); - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << " ht=" << height << " mismatched CCvout scriptPubKey=" << addr0 << " vs tx.vout[0].scriptPubKey=" << addr1 << " opretpk.size=" << opretpk.size() << " opretpk=" << HexStr(opretpk) << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << " ht=" << height << " MarmaraUnlockht=" << MarmaraUnlockht(height) << " vs opret's ht=" << ht << " unlock=" << unlockht << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << " ht=" << height << " error decoding coinbase opret" << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << " ht=" << height << " incorrect vout size for marmara coinbase" << std::endl); - - errmsg = "marmara cc constrains even height blocks to pay 100%% to CC in vout0 with opreturn"; - return(-1); - } -} - -// check marmara stake tx -// stake tx should have one cc vout and optional opret (in this case it is the cc opret) -// stake tx points to staking utxo in vintx -// stake tx vout[0].scriptPubKey equals the referred staking utxo scriptPubKey -// and opret equals to the opret in the last vout or to the ccopret in the referred staking tx -// see komodo_staked() where stake tx is created -int32_t MarmaraValidateStakeTx(const char *destaddr, const CScript &vintxOpret, const CTransaction &staketx, int32_t height) -// note: the opret is fetched in komodo_txtime from cc opret or the last vout. -// And that opret was added to stake tx by MarmaraSignature() -{ - uint8_t funcid; - char pkInOpretAddr[KOMODO_ADDRESS_BUFSIZE]; - const int32_t MARMARA_STAKE_TX_OK = 1; - const int32_t MARMARA_NOT_STAKE_TX = 0; - - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "staketxid=" << staketx.GetHash().ToString() << " numvins=" << staketx.vin.size() << " numvouts=" << staketx.vout.size() << " vout[0].nValue=" << staketx.vout[0].nValue << " inOpret.size=" << vintxOpret.size() << std::endl); - //old code: if (staketx.vout.size() == 2 && inOpret == staketx.vout[1].scriptPubKey) - - //check stake tx: - /*bool checkStakeTxVout = false; - if (strcmp(ASSETCHAINS_SYMBOL, "MARMARAXY5") == 0 && height < 2058) - checkStakeTxVout = (staketx.vout.size() == 2); // old blocks stake txns have last vout opret - else - checkStakeTxVout = (staketx.vout.size() == 1); // stake txns have cc vout opret */ - - if (staketx.vout.size() == 1 && staketx.vout[0].scriptPubKey.IsPayToCryptoCondition()) - { - CScript opret; - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - CPubKey Marmarapk = GetUnspendable(cp, 0); - CPubKey opretpk; - - // for stake tx check only cc opret, in last-vout opret there is pos data: - CMarmaraActivatedOpretChecker activatedChecker; - CMarmaraLockInLoopOpretChecker lockinloopChecker(CHECK_ONLY_CCOPRET); - - if (get_either_opret(&activatedChecker, staketx, 0, opret, opretpk)) - { - if (vintxOpret != opret) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "found activated opret not equal to vintx opret, opret=" << opret.ToString() << std::endl); - return MARMARA_NOT_STAKE_TX; - } - - //int32_t height, unlockht; - //funcid = DecodeMarmaraCoinbaseOpRet(opret, senderpk, height, unlockht); - GetCCaddress1of2(cp, pkInOpretAddr, Marmarapk, opretpk); - - if (strcmp(destaddr, pkInOpretAddr) != 0) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "found bad activated opret" << " destaddr=" << destaddr << " not equal to 1of2 addr for pk in opret=" << pkInOpretAddr << std::endl); - return MARMARA_NOT_STAKE_TX; - } - else - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "found correct activated opret" << " destaddr=" << destaddr << std::endl); - - return MARMARA_STAKE_TX_OK; - } - else if (get_either_opret(&lockinloopChecker, staketx, 0, opret, opretpk)) - { - - if (vintxOpret != opret) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "found bad lock-in-loop opret not equal to vintx opret, opret=" << opret.ToString() << std::endl); - return MARMARA_NOT_STAKE_TX; - } - - struct SMarmaraCreditLoopOpret loopData; - MarmaraDecodeLoopOpret(opret, loopData); - CPubKey createtxidPk = CCtxidaddr_tweak(NULL, loopData.createtxid); - GetCCaddress1of2(cp, pkInOpretAddr, Marmarapk, createtxidPk); - - if (strcmp(destaddr, pkInOpretAddr) != 0) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "found bad locked-in-loop opret" << " destaddr=" << destaddr << " not equal to 1of2 addr for pk in opret=" << pkInOpretAddr << std::endl); - return MARMARA_NOT_STAKE_TX; - } - else - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "found correct locked-in-loop opret" << " destaddr=" << destaddr << std::endl); - - return MARMARA_STAKE_TX_OK; - } - } - - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "incorrect stake tx vout num" << " stake txid=" << staketx.GetHash().GetHex() << " inOpret=" << vintxOpret.ToString() << std::endl); - return MARMARA_NOT_STAKE_TX; -} - -#define MAKE_ACTIVATED_WALLET_DATA(key, pk, addr, segid, amount) std::make_tuple(key, pk, addr, segid, amount) - -#define ACTIVATED_WALLET_DATA_KEY(d) std::get<0>(d) -#define ACTIVATED_WALLET_DATA_PK(d) std::get<1>(d) -#define ACTIVATED_WALLET_DATA_ADDR(d) std::get<2>(d) -#define ACTIVATED_WALLET_DATA_SEGID(d) std::get<3>(d) -#define ACTIVATED_WALLET_DATA_AMOUNT(d) std::get<4>(d) - -typedef std::tuple tACTIVATED_WALLET_DATA; -typedef std::vector vACTIVATED_WALLET_DATA; - -// enum activated 1of2 addr in the wallet: -static void EnumWalletActivatedAddresses(CWallet *pwalletMain, vACTIVATED_WALLET_DATA &activated) -{ - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - CPubKey marmarapk = GetUnspendable(cp, 0); - - std::set setKeyIds; - pwalletMain->GetKeys(setKeyIds); - for (const auto &keyid : setKeyIds) - { - //std::cerr << "key=" << keyid.ToString() << std::endl; - CPubKey pk; - if (pwalletMain->GetPubKey(keyid, pk)) - { - CKey key; - pwalletMain->GetKey(keyid, key); - - CMutableTransaction mtx; - std::vector pubkeys; - char activated1of2addr[KOMODO_ADDRESS_BUFSIZE]; - GetCCaddress1of2(cp, activated1of2addr, marmarapk, pk); - CAmount amount = AddMarmaraCCInputs(IsMarmaraActivatedVout, mtx, pubkeys, activated1of2addr, 0, CC_MAXVINS); - if (amount > 0) - { - uint32_t segid = komodo_segid32(activated1of2addr) & 0x3f; - tACTIVATED_WALLET_DATA tuple = MAKE_ACTIVATED_WALLET_DATA(key, pk, std::string(activated1of2addr), segid, amount); - activated.push_back(tuple); - } - memset(&key, '\0', sizeof(key)); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "can't get pubkey from the wallet for keyid=" << keyid.ToString() << std::endl); - } -} - - -static void EnumAllActivatedAddresses(std::vector &activatedAddresses) -{ - char markeraddr[KOMODO_ADDRESS_BUFSIZE]; - std::vector > markerOutputs; - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - //CPubKey mypk = pubkey2pk(Mypubkey()); - CPubKey Marmarapk = GetUnspendable(cp, NULL); - - GetCCaddress(cp, markeraddr, Marmarapk); - SetCCunspents(markerOutputs, markeraddr, true); - std::set userpks; - - // get all pubkeys who have ever activated coins: - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "checking markeraddr=" << markeraddr << std::endl); - for (std::vector >::const_iterator it = markerOutputs.begin(); it != markerOutputs.end(); it++) - { - CTransaction activatetx; - uint256 hashBlock; - uint256 marker_txid = it->first.txhash; - int32_t marker_nvout = (int32_t)it->first.index; - CAmount marker_amount = it->second.satoshis; - - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "checking tx on markeraddr txid=" << marker_txid.GetHex() << " vout=" << marker_nvout << std::endl); - if (marker_amount == MARMARA_ACTIVATED_MARKER_AMOUNT) - { - if (myGetTransaction(marker_txid, activatetx, hashBlock) && !hashBlock.IsNull()) - { - for(int32_t i = 0; i < activatetx.vout.size(); i++) - { - if (activatetx.vout[i].scriptPubKey.IsPayToCryptoCondition()) - { - CScript opret; - CPubKey opretpk; - CMarmaraActivatedOpretChecker activatedChecker; - - if (get_either_opret(&activatedChecker, activatetx, i, opret, opretpk)) - { - userpks.insert(opretpk); - } - } - } - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "error getting activated tx=" << marker_txid.GetHex() << ", in mempool=" << hashBlock.IsNull() << std::endl); - } - } - - - // create all activated addresses: - for (auto const &pk : userpks) { - char activatedaddr[KOMODO_ADDRESS_BUFSIZE]; - GetCCaddress1of2(cp, activatedaddr, Marmarapk, pk); - activatedAddresses.push_back(activatedaddr); - } - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "found activated addresses=" << activatedAddresses.size() << std::endl); -} - - -// enumerates activated cc vouts in the wallet or on mypk if wallet is not available -// calls a callback allowing to do something with the utxos (add to staking utxo array) -// TODO: maybe better to use AddMarmaraCCInputs with a callback for unification... -template -static void EnumActivatedCoins(T func, bool onlyLocal) -{ - std::vector activatedAddresses; -#ifdef ENABLE_WALLET - if (onlyLocal) - { - if (pwalletMain) - { - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - vACTIVATED_WALLET_DATA activated; - EnumWalletActivatedAddresses(pwalletMain, activated); - for (const auto &a : activated) - activatedAddresses.push_back(ACTIVATED_WALLET_DATA_ADDR(a)); - } - else - { - // should not be here as it can't be PoS without a wallet - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "wallet not available" << std::endl); - return; - } - } -#endif - - if (!onlyLocal) - EnumAllActivatedAddresses(activatedAddresses); - - for (const auto &addr : activatedAddresses) - { - // add activated coins: - std::vector > activatedOutputs; - SetCCunspents(activatedOutputs, (char*)addr.c_str(), true); - - // add my activated coins: - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "checking activatedaddr=" << addr << std::endl); - for (std::vector >::const_iterator it = activatedOutputs.begin(); it != activatedOutputs.end(); it++) - { - CTransaction tx; uint256 hashBlock; - CBlockIndex *pindex; - - uint256 txid = it->first.txhash; - int32_t nvout = (int32_t)it->first.index; - CAmount nValue = it->second.satoshis; - - if (nValue < COIN) // skip small values - continue; - - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "check tx on activatedaddr with txid=" << txid.GetHex() << " vout=" << nvout << std::endl); - - if (myGetTransaction(txid, tx, hashBlock) && (pindex = komodo_getblockindex(hashBlock)) != 0 && myIsutxo_spentinmempool(ignoretxid, ignorevin, txid, nvout) == 0) - { - char utxoaddr[KOMODO_ADDRESS_BUFSIZE] = ""; - - Getscriptaddress(utxoaddr, tx.vout[nvout].scriptPubKey); - if (strcmp(addr.c_str(), utxoaddr) == 0) // check if actual vout address matches the address in the index - // because a key from vSolution[1] could appear in the addressindex and it does not match the address. - // This is fixed in this marmara branch but this fix is for discussion - { - CScript opret; - CPubKey opretpk; - CMarmaraActivatedOpretChecker activatedChecker; - - if (get_either_opret(&activatedChecker, tx, nvout, opret, opretpk)) - { - // call callback function: - func(addr.c_str(), tx, nvout, pindex); - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "found my activated 1of2 addr txid=" << txid.GetHex() << " vout=" << nvout << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "skipped activated 1of2 addr txid=" << txid.GetHex() << " vout=" << nvout << " cant decode opret" << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "skipped activated 1of2 addr txid=" << txid.GetHex() << " vout=" << nvout << " utxo addr and index not matched" << std::endl); - } - } - } -} - -// enumerates pk's locked in loop cc vouts -// pk could be null then all LCL coins enumerated -// calls a callback allowing to do something with the utxos (add to staking utxo array) -// TODO: maybe better to use AddMarmaraCCInputs with a callback for unification... -template -static void EnumLockedInLoop(T func, const CPubKey &pk) -{ - char markeraddr[KOMODO_ADDRESS_BUFSIZE]; - std::vector > markerOutputs; - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - // CPubKey mypk = pubkey2pk(Mypubkey()); - CPubKey Marmarapk = GetUnspendable(cp, NULL); - - GetCCaddress(cp, markeraddr, Marmarapk); - SetCCunspents(markerOutputs, markeraddr, true); - - // enum all createtxids: - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "checking markeraddr=" << markeraddr << std::endl); - for (std::vector >::const_iterator it = markerOutputs.begin(); it != markerOutputs.end(); it++) - { - CTransaction isssuancetx; - uint256 hashBlock; - uint256 marker_txid = it->first.txhash; - int32_t marker_nvout = (int32_t)it->first.index; - CAmount marker_amount = it->second.satoshis; - - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "checking tx on markeraddr txid=" << marker_txid.GetHex() << " vout=" << marker_nvout << std::endl); - if (marker_nvout == MARMARA_MARKER_VOUT && marker_amount == MARMARA_LOOP_MARKER_AMOUNT) - { - if (myGetTransaction(marker_txid, isssuancetx, hashBlock) && !hashBlock.IsNull()) - { - if (!isssuancetx.IsCoinBase() && isssuancetx.vout.size() > 2 && isssuancetx.vout.back().nValue == 0 /*has opret*/) - { - struct SMarmaraCreditLoopOpret loopData; - // get createtxid from the issuance tx - if (MarmaraDecodeLoopOpret(isssuancetx.vout.back().scriptPubKey, loopData) == MARMARA_ISSUE) - { - char loopaddr[KOMODO_ADDRESS_BUFSIZE]; - std::vector > loopOutputs; - CPubKey createtxidPk = CCtxidaddr_tweak(NULL, loopData.createtxid); - - // enum unspents in the loop - GetCCaddress1of2(cp, loopaddr, Marmarapk, createtxidPk); - SetCCunspents(loopOutputs, loopaddr, true); - - // enum all locked-in-loop addresses: - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "checking on loopaddr=" << loopaddr << std::endl); - for (std::vector >::const_iterator it = loopOutputs.begin(); it != loopOutputs.end(); it++) - { - CTransaction looptx; - uint256 hashBlock; - CBlockIndex *pindex; - uint256 txid = it->first.txhash; - int32_t nvout = (int32_t)it->first.index; - - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "checking tx on loopaddr txid=" << txid.GetHex() << " vout=" << nvout << std::endl); - - if (myGetTransaction(txid, looptx, hashBlock) && (pindex = komodo_getblockindex(hashBlock)) != 0 && !myIsutxo_spentinmempool(ignoretxid, ignorevin, txid, nvout)) - { - /* lock-in-loop cant be mined */ /* now it could be cc opret, not necessary OP_RETURN vout in the back */ - if (!looptx.IsCoinBase() && looptx.vout.size() > 0 /* && looptx.vout.back().nValue == 0 */) - { - char utxoaddr[KOMODO_ADDRESS_BUFSIZE] = ""; - - Getscriptaddress(utxoaddr, looptx.vout[nvout].scriptPubKey); - - // NOTE: This is checking if the real spk address matches the index address - // because other keys from the vout.spk could be used in the addressindex) - // spk structure (keys): hashed-cc, pubkey, ccopret - // For the marmara branch I disabled getting other keys except the first in ExtractDestination but this is debatable - if (strcmp(loopaddr, utxoaddr) == 0) - { - CScript opret; - CPubKey pk_in_opret; - - // get pk from cc opret or last vout opret - // use pk only from cc opret (which marks vout with owner), do not use the last vout opret if no cc opret somehow - CMarmaraLockInLoopOpretChecker lockinloopChecker(CHECK_ONLY_CCOPRET); - if (get_either_opret(&lockinloopChecker, looptx, nvout, opret, pk_in_opret)) - { - if (!pk.IsValid() || pk == pk_in_opret) // check pk in opret - { - // call callback func: - func(loopaddr, looptx, nvout, pindex); - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "found my lock-in-loop 1of2 addr txid=" << txid.GetHex() << " vout=" << nvout << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "skipped lock-in-loop 1of2 addr txid=" << txid.GetHex() << " vout=" << nvout << " does not match the pk" << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "skipped lock-in-loop 1of2 addr txid=" << txid.GetHex() << " vout=" << nvout << " can't decode opret" << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "skipped lock-in-loop 1of2 addr txid=" << txid.GetHex() << " vout=" << nvout << " utxo addr and address index not matched" << std::endl); - } - } - } - } - } - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "error getting issuance tx=" << marker_txid.GetHex() << ", in mempool=" << hashBlock.IsNull() << std::endl); - } - } -} - - -// add marmara special UTXO from activated and lock-in-loop addresses for staking -// called from PoS code -struct komodo_staking *MarmaraGetStakingUtxos(struct komodo_staking *array, int32_t *numkp, int32_t *maxkp, uint8_t *hashbuf) -{ - const char *logFName = __func__; - const bool onlyLocalUtxos = true; - const CPubKey emptypk; - - // add all activated utxos: - //std::cerr << " entered" << std::endl; - EnumActivatedCoins( - [&](const char *activatedaddr, const CTransaction & tx, int32_t nvout, CBlockIndex *pindex) - { - array = komodo_addutxo(array, numkp, maxkp, (uint32_t)pindex->nTime, (uint64_t)tx.vout[nvout].nValue, tx.GetHash(), nvout, (char*)activatedaddr, hashbuf, tx.vout[nvout].scriptPubKey); - LOGSTREAM("marmara", CCLOG_DEBUG2, stream << logFName << " " << "added utxo for staking activated 1of2 addr txid=" << tx.GetHash().GetHex() << " vout=" << nvout << std::endl); - }, - !onlyLocalUtxos - ); - - // add all lock-in-loops utxos: - EnumLockedInLoop( - [&](const char *loopaddr, const CTransaction & tx, int32_t nvout, CBlockIndex *pindex) - { - array = komodo_addutxo(array, numkp, maxkp, (uint32_t)pindex->nTime, (uint64_t)tx.vout[nvout].nValue, tx.GetHash(), nvout, (char*)loopaddr, hashbuf, tx.vout[nvout].scriptPubKey); - LOGSTREAM("marmara", CCLOG_DEBUG2, stream << logFName << " " << "added utxo for staking locked-in-loop 1of2addr txid=" << tx.GetHash().GetHex() << " vout=" << nvout << std::endl); - }, - emptypk - ); - - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "added " << *numkp << " utxos for staking" << std::endl); - return array; -} - -// returns stake preferences for activated and locked utxos -int32_t MarmaraGetStakeMultiplier(const CTransaction & staketx, int32_t nvout) -{ - CScript opret; - CPubKey opretpk; - CMarmaraActivatedOpretChecker activatedChecker; - CMarmaraLockInLoopOpretChecker lockinloopChecker(CHECK_ONLY_CCOPRET); // for stake tx check only cc opret, in last-vout opret there is pos data - - if (nvout >= 0 && nvout < staketx.vout.size()) // check boundary - { - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "check staketx txid=" << staketx.GetHash().GetHex() << std::endl); - if (staketx.vout[nvout].scriptPubKey.IsPayToCryptoCondition()) - { - if (get_either_opret(&lockinloopChecker, staketx, nvout, opret, opretpk) /*&& mypk == opretpk - not for validation */) // check if opret is lock-in-loop vout - { - LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "check locked-in-loop opret okay, pk=" << HexStr(opretpk) << std::endl); - - struct SMarmaraCreditLoopOpret loopData; - if (MarmaraDecodeLoopOpret(opret, loopData) != 0) - { - //LOGSTREAMFN("marmara", CCLOG_DEBUG3, stream << "decode LCL opret okay" << std::endl); - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - CPubKey Marmarapk = GetUnspendable(cp, NULL); - - // get LCL address - char lockInLoop1of2addr[KOMODO_ADDRESS_BUFSIZE]; - CPubKey createtxidPk = CCtxidaddr_tweak(NULL, loopData.createtxid); - GetCCaddress1of2(cp, lockInLoop1of2addr, Marmarapk, createtxidPk); - - // get vout address - char ccvoutaddr[KOMODO_ADDRESS_BUFSIZE]; - Getscriptaddress(ccvoutaddr, staketx.vout[nvout].scriptPubKey); - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "ccvoutaddr=" << ccvoutaddr << " lockInLoop1of2addr=" << lockInLoop1of2addr << std::endl); - - if (strcmp(lockInLoop1of2addr, ccvoutaddr) == 0) // check vout address is lock-in-loop address - { - int32_t mult = 3; - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "utxo picked for stake x" << mult << " as locked-in-loop" << " txid=" << staketx.GetHash().GetHex() << " nvout=" << nvout << std::endl); - return mult; // 3x multiplier for lock-in-loop - } - } - } - else if (get_either_opret(&activatedChecker, staketx, nvout, opret, opretpk)) // check if this is activated vout - { - if (staketx.vout[nvout].scriptPubKey.IsPayToCryptoCondition()) - { - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - CPubKey Marmarapk = GetUnspendable(cp, NULL); - - char activated1of2addr[KOMODO_ADDRESS_BUFSIZE]; - char ccvoutaddr[KOMODO_ADDRESS_BUFSIZE]; - GetCCaddress1of2(cp, activated1of2addr, Marmarapk, opretpk/* mypk*/); - Getscriptaddress(ccvoutaddr, staketx.vout[nvout].scriptPubKey); - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "ccvoutaddr=" << ccvoutaddr << " activated1of2addr=" << activated1of2addr << std::endl); - - if (strcmp(activated1of2addr, ccvoutaddr) == 0) // check vout address is opretpk activated address - { - vscript_t vopret; - uint8_t funcid = 0; - int32_t mult = 1; - GetOpReturnData(opret, vopret); - - if (vopret.size() >= 2) - funcid = vopret[1]; - - if (IsFuncidOneOf(funcid, { MARMARA_COINBASE_3X /*, MARMARA_ACTIVATED_3X*/ })) // is 3x stake tx? - mult = 3; - - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "utxo picked for stake x" << mult << " as activated" << " txid=" << staketx.GetHash().GetHex() << " nvout=" << nvout << std::endl); - return mult; // 1x or 3x multiplier for activated - } - } - } - } - } - - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "utxo not recognized for marmara stake" << " txid=" << staketx.GetHash().GetHex() << " nvout=" << nvout << std::endl); - return 1; //default multiplier 1x -} - - -// make activated by locking the amount on the max block height -UniValue MarmaraLock(const CPubKey &remotepk, int64_t txfee, int64_t amount, const CPubKey ¶mPk) -{ - CMutableTransaction tmpmtx, mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp, C; - CPubKey Marmarapk, mypk, destPk; - //int32_t unlockht, /*refunlockht,*/ nvout, ht, numvouts; - int64_t nValue, val, inputsum = 0, remains, change = 0; - std::string rawtx, errorstr; - char mynormaladdr[KOMODO_ADDRESS_BUFSIZE], activated1of2addr[KOMODO_ADDRESS_BUFSIZE]; - uint256 txid, hashBlock; - CTransaction tx; - uint8_t funcid; - - if (txfee == 0) - txfee = 10000; - - int32_t height = komodo_nextheight(); - // as opret creation function MarmaraCoinbaseOpret creates opret only for even blocks - adjust this base height to even value - if ((height & 1) != 0) - height++; - - cp = CCinit(&C, EVAL_MARMARA); - Marmarapk = GetUnspendable(cp, 0); - - bool isRemote = IS_REMOTE(remotepk); - if (isRemote) - mypk = remotepk; - else - mypk = pubkey2pk(Mypubkey()); - - if (paramPk.IsValid()) - destPk = paramPk; - else - destPk = mypk; // lock to self - - Getscriptaddress(mynormaladdr, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG); - if ((val = CCaddress_balance(mynormaladdr, 0)) < amount) // if not enough funds in the wallet - val -= 2 * txfee + MARMARA_ACTIVATED_MARKER_AMOUNT; // dont take all, should al least 1 txfee remained - else - val = amount; - - CAmount amountToAdd = val + MARMARA_ACTIVATED_MARKER_AMOUNT; - if (val > txfee) - { - inputsum = AddNormalinputs(mtx, mypk, amountToAdd + txfee, MARMARA_VINS, isRemote); //added '+txfee' because if 'inputsum' exactly was equal to 'val' we'd exit from insufficient funds - /* do not need this as threshold removed from Addnormalinputs - if (inputsum < val + txfee) { - // if added inputs are insufficient - // try to add value and txfee separately: - mtx.vin.clear(); - inputsum = AddNormalinputs(mtx, mypk, val, CC_MAXVINS / 2, isRemote); - inputsum += AddNormalinputs(mtx, mypk, txfee, 5, isRemote); - }*/ - } - //fprintf(stderr,"%s added normal inputs=%.8f required val+txfee=%.8f\n", logFuncName, (double)inputsum/COIN,(double)(val+txfee)/COIN); - - CScript opret = MarmaraCoinbaseOpret(MARMARA_ACTIVATED, height, destPk); - // lock the amount on 1of2 address: - mtx.vout.push_back(MakeMarmaraCC1of2voutOpret(val, destPk, opret)); //add coinbase opret - mtx.vout.push_back(MakeCC1vout(EVAL_MARMARA, MARMARA_ACTIVATED_MARKER_AMOUNT, Marmarapk)); - - /* not used old code: adding from funds locked for another height - if (inputsum < amount + txfee) // if not enough normal inputs for collateral - { - //refunlockht = MarmaraUnlockht(height); // randomized - - result.push_back(Pair("normalfunds", ValueFromAmount(inputsum))); - result.push_back(Pair("height", static_cast(height))); - //result.push_back(Pair("unlockht", refunlockht)); - - // fund remainder to add: - remains = (amount + txfee) - inputsum; - - std::vector > unspentOutputs; - GetCCaddress1of2(cp, activated1of2addr, Marmarapk, mypk); - SetCCunspents(unspentOutputs, activated1of2addr, true); - //threshold = remains / (MARMARA_VINS + 1); - uint8_t mypriv[32]; - Myprivkey(mypriv); - CCaddr1of2set(cp, Marmarapk, mypk, mypriv, activated1of2addr); - memset(mypriv,0,sizeof(mypriv)); - } - */ - - if (inputsum >= amountToAdd + txfee) - { - if (inputsum > amountToAdd + txfee) - { - change = (inputsum - amountToAdd); - mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - } - rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, CScript()/*opret moved to cc vout*/); - if (rawtx.size() == 0) - { - errorstr = "couldnt finalize CCtx"; - } - else - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", rawtx)); - return(result); - } - } - else - errorstr = (char *)"insufficient funds"; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", errorstr)); - return(result); -} - -// add stake tx opret, finalize and sign stake tx on activated or lock-in-loop 1of2 addr -// (note: utxosig bufsize = 512 is checked) -int32_t MarmaraSignature(uint8_t *utxosig, CMutableTransaction &mstaketx) -{ - uint256 txid, hashBlock; - CTransaction vintx; - int64_t txfee = 10000; - - // compatibility rules: - - // for marmara testers chain - /*bool lastVoutOpretDiscontinued = true; - if (strcmp(ASSETCHAINS_SYMBOL, "MCL0") == 0) - { - CBlockIndex *tipindex = chainActive.Tip(); - if (tipindex) - { - if (tipindex->GetHeight() + 1 < 2000) - { - lastVoutOpretDiscontinued = false; - } - } - }*/ - // end of compatibility rules - - if (myGetTransaction(mstaketx.vin[0].prevout.hash, vintx, hashBlock) && vintx.vout.size() > 0 /*was >1, but if ccopret could be only 1 vout*/ && mstaketx.vin[0].prevout.n < vintx.vout.size()) - { - CScript finalOpret, vintxOpret; - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - uint8_t marmarapriv[32]; - CPubKey Marmarapk = GetUnspendable(cp, marmarapriv); - - CPubKey mypk = pubkey2pk(Mypubkey()); // no spending from mypk or any change to it is supposed, it is used just as FinalizeCCTx requires such param - CPubKey opretpk; - CMarmaraActivatedOpretChecker activatedChecker; - CMarmaraLockInLoopOpretChecker lockinloopChecker; - - if (get_either_opret(&activatedChecker, vintx, mstaketx.vin[0].prevout.n, vintxOpret, opretpk)) // note: opret should be ONLY in vintx ccvout - { - // sign activated staked utxo - - // decode utxo 1of2 address - char activated1of2addr[KOMODO_ADDRESS_BUFSIZE]; - - /* will use marmara pk to work with remote nspv users - CKeyID keyid = opretpk.GetID(); - CKey privkey; - - if (!pwalletMain || !pwalletMain->GetKey(keyid, privkey)) - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "can't find user privkey or wallet not available" << std::endl); - return 0; - } */ - - - //LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "vintx opretpk=" << HexStr(opretpk) << " keyId=" << keyid.GetHex() << " privkey=" << HexStr(privkey) << std::endl); - //CPubKey pk0 = pubkey2pk(ParseHex("03f8c7b24729101443500bcb26171a65ab070e1b424bfd8c1830b0ba42d9491703")); - //LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "for 03f8 pk keyId=" << pk0.GetID().GetHex() << std::endl); - - // this is for transition period to cc-vout opret in stake txns - // if vintx has the last-vout opret then move it to cc-vout opret - // check if cc vout opret exists in mtx - CScript opret; - bool hasccopret = false; - if (GetCCOpReturnData(mstaketx.vout[0].scriptPubKey, opret)) - { - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "compatibility code: in mtx found ccopret=" << opret.ToString() << std::endl); - if (activatedChecker.CheckOpret(opret, opretpk)) - { - hasccopret = true; - } - } - // if mtx does not have cc opret then add it - /*if (!hasccopret && lastVoutOpretDiscontinued) - { - // add cc opret to stake tx: - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "compatibility code added ccopret to mtx" << std::endl); - mtx.vout[0] = MakeMarmaraCC1of2voutOpret(mtx.vout[0].nValue, opretpk, vintxOpret); - }*/ - - Getscriptaddress(activated1of2addr, mstaketx.vout[0].scriptPubKey); - - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "found activated opret in staking vintx" << std::endl); - - CC *probeCond = MakeCCcond1of2(EVAL_MARMARA, Marmarapk, opretpk); - // use the global pk (instead of privkey for user's pubkey from the wallet): - CCAddVintxCond(cp, probeCond, marmarapriv/*privkey.begin()*/); //add probe condition to sign vintx 1of2 utxo - cc_free(probeCond); - - //if (lastVoutOpretDiscontinued) - finalOpret = CScript(); //empty for activated - //else - // finalOpret = vintxOpret; // last-vout opret continues to be used until some height - - // memset(&privkey, '\0', sizeof(privkey)); - - } - else if (get_either_opret(&lockinloopChecker, vintx, mstaketx.vin[0].prevout.n, vintxOpret, opretpk)) // note: opret could be in vintx ccvout - { - // sign lock-in-loop utxo - - struct SMarmaraCreditLoopOpret loopData; - MarmaraDecodeLoopOpret(vintxOpret, loopData); - - CPubKey createtxidPk = CCtxidaddr_tweak(NULL, loopData.createtxid); - - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "found locked-in-loop opret in staking vintx" << std::endl); - - CC *probeCond = MakeCCcond1of2(EVAL_MARMARA, Marmarapk, createtxidPk); - CCAddVintxCond(cp, probeCond, marmarapriv); //add probe condition to sign vintx 1of2 utxo - cc_free(probeCond); - - //if (lastVoutOpretDiscontinued) - finalOpret = CScript(); // empty last vout opret - //else - // finalOpret = vintxOpret; // last-vout opret continues to be used until some height - } - - // note: opreturn for stake tx is taken from the staking utxo (ccvout or back): - std::string rawtx = FinalizeCCTx(0, cp, mstaketx, mypk, txfee, finalOpret); // opret for LCL or empty for activated - if (rawtx.size() > 0) - { - int32_t siglen = mstaketx.vin[0].scriptSig.size(); - uint8_t *scriptptr = &mstaketx.vin[0].scriptSig[0]; - - if (siglen > 512) { // check sig buffer limit - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "scriptSig length is more than utxosig bufsize, truncated! siglen=" << siglen << std::endl); - siglen = 512; - } - - std::ostringstream debstream; - for (int32_t i = 0; i < siglen; i++) { - utxosig[i] = scriptptr[i]; - debstream << std::hex << (int)scriptptr[i]; + utxosig[i] = ptr[i]; + //fprintf(stderr,"%02x",ptr[i]); } - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "scriptSig=" << debstream.str() << " got signed rawtx=" << rawtx << " siglen=" << siglen << std::endl); + //fprintf(stderr," got signed rawtx.%s siglen.%d\n",rawtx.c_str(),siglen); return(siglen); } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "cannot sign marmara staked tx, bad mtx=" << HexStr(E_MARSHAL(ss << mstaketx)) << " opretpk=" << HexStr(opretpk) << std::endl); } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "cannot get vintx for staked tx" << std::endl); return(0); } // jl777: decide on what unlockht settlement change should have -> from utxo making change -UniValue MarmaraSettlement(int64_t txfee, uint256 refbatontxid, CTransaction &settlementTx) +UniValue MarmaraSettlement(uint64_t txfee,uint256 refbatontxid) { - UniValue result(UniValue::VOBJ); - std::vector creditloop; - uint256 batontxid; - int32_t numerrs = 0, numDebtors; - std::string rawtx; - char loop1of2addr[KOMODO_ADDRESS_BUFSIZE], myCCaddr[KOMODO_ADDRESS_BUFSIZE], destaddr[KOMODO_ADDRESS_BUFSIZE], batonCCaddr[KOMODO_ADDRESS_BUFSIZE]; - struct CCcontract_info *cp, C; - - if (txfee == 0) + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::vector creditloop; uint256 batontxid,createtxid,refcreatetxid,hashBlock; uint8_t funcid; int32_t numerrs=0,i,n,numvouts,matures,refmatures,height; int64_t amount,refamount,remaining,inputsum,change; CPubKey Marmarapk,mypk,pk; std::string currency,refcurrency,rawtx; CTransaction tx,batontx; char coinaddr[64],myCCaddr[64],destaddr[64],batonCCaddr[64],str[2],txidaddr[64]; std::vector pubkeys; struct CCcontract_info *cp,C; + if ( txfee == 0 ) txfee = 10000; - - cp = CCinit(&C, EVAL_MARMARA); - CPubKey minerpk = pubkey2pk(Mypubkey()); - uint8_t marmarapriv[32]; - CPubKey Marmarapk = GetUnspendable(cp, marmarapriv); - - int64_t change = 0; - //int32_t height = chainActive.LastTip()->GetHeight(); - if ((numDebtors = MarmaraGetbatontxid(creditloop, batontxid, refbatontxid)) > 0) + cp = CCinit(&C,EVAL_MARMARA); + mypk = pubkey2pk(Mypubkey()); + Marmarapk = GetUnspendable(cp,0); + remaining = change = 0; + height = chainActive.LastTip()->GetHeight(); + if ( (n= MarmaraGetbatontxid(creditloop,batontxid,refbatontxid)) > 0 ) { - CTransaction batontx; - uint256 hashBlock; - struct SMarmaraCreditLoopOpret loopData; - - if( get_loop_creation_data(creditloop[0], loopData) == 0 ) + if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && (numvouts= batontx.vout.size()) > 1 ) { - if (myGetTransaction(batontxid, batontx, hashBlock) && !hashBlock.IsNull() && batontx.vout.size() > 1) + if ( (funcid= MarmaraDecodeLoopOpret(batontx.vout[numvouts-1].scriptPubKey,refcreatetxid,pk,refamount,refmatures,refcurrency)) != 0 ) { - uint8_t funcid; - - if ((funcid = MarmaraDecodeLoopOpret(batontx.vout.back().scriptPubKey, loopData)) != 0) // update loopData with the baton opret + if ( refcreatetxid != creditloop[0] ) { - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - if (loopData.createtxid != creditloop[0]) - { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "invalid opret createtxid, should be set to creditloop[0]")); //TODO: note change - return(result); - } - else if (chainActive.LastTip()->GetHeight() < loopData.matures) - { - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "loop doesnt mature for another " << loopData.matures - chainActive.LastTip()->GetHeight() << " blocks" << std::endl); - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "cant settle immature creditloop")); - return(result); - } - else if ((loopData.matures & 1) == 0) - { - // discontinued: - //result.push_back(Pair("result", "error")); - //result.push_back(Pair("error", "cant automatic settle even maturity heights")); - //return(result); - } - else if (numDebtors < 1) - { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "creditloop too short")); - return(result); - } - GetCCaddress(cp, myCCaddr, Mypubkey()); - Getscriptaddress(batonCCaddr, batontx.vout[0].scriptPubKey); - - // allow any miner to settle, do not check mypk: - //if (strcmp(myCCaddr, batonCCaddr) == 0) // if mypk user owns the baton - //{ - std::vector pubkeys; - uint256 issuetxid; - - // note: can't spend the baton any more as settlement could be done by any miner - // spend the marker on marmara global pk - if (numDebtors > 1) - issuetxid = creditloop[1]; - else - issuetxid = batontxid; - mtx.vin.push_back(CTxIn(issuetxid, MARMARA_OPENCLOSE_VOUT, CScript())); // spend vout2 marker - close the loop - - // add tx fee from mypubkey - if (AddNormalinputs2(mtx, txfee, 4) < txfee) { // TODO: in the previous code txfee was taken from 1of2 address - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "cant add normal inputs for txfee")); - return(result); - } - - char lockInLoop1of2addr[KOMODO_ADDRESS_BUFSIZE]; - CPubKey createtxidPk = CCtxidaddr_tweak(NULL, loopData.createtxid); - GetCCaddress1of2(cp, lockInLoop1of2addr, Marmarapk, createtxidPk); // 1of2 lock-in-loop address - - CC *lockInLoop1of2cond = MakeCCcond1of2(EVAL_MARMARA, Marmarapk, createtxidPk); - CCAddVintxCond(cp, lockInLoop1of2cond, marmarapriv); //add probe condition to spend from the lock-in-loop address - cc_free(lockInLoop1of2cond); - - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "calling AddMarmaraCCInputs for lock-in-loop addr=" << lockInLoop1of2addr << " adding amount=" << loopData.amount << std::endl); - CAmount lclAmount = AddMarmaraCCInputs(IsMarmaraLockedInLoopVout, mtx, pubkeys, lockInLoop1of2addr, loopData.amount, MARMARA_VINS); - if (lclAmount >= loopData.amount) + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"invalid refcreatetxid, setting to creditloop[0]")); + return(result); + } + else if ( chainActive.LastTip()->GetHeight() < refmatures ) + { + fprintf(stderr,"doesnt mature for another %d blocks\n",refmatures - chainActive.LastTip()->GetHeight()); + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"cant settle immature creditloop")); + return(result); + } + else if ( (refmatures & 1) == 0 ) + { + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"cant automatic settle even maturity heights")); + return(result); + } + else if ( n < 1 ) + { + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"creditloop too short")); + return(result); + } + remaining = refamount; + GetCCaddress(cp,myCCaddr,Mypubkey()); + Getscriptaddress(batonCCaddr,batontx.vout[0].scriptPubKey); + if ( strcmp(myCCaddr,batonCCaddr) == 0 ) + { + mtx.vin.push_back(CTxIn(n == 1 ? batontxid : creditloop[1],1,CScript())); // issuance marker + pubkeys.push_back(Marmarapk); + mtx.vin.push_back(CTxIn(batontxid,0,CScript())); + pubkeys.push_back(mypk); + for (i=1; i txfee) { - CScript opret; - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "error: change not null=" << change << ", sent back to lock-in-loop addr=" << lockInLoop1of2addr << std::endl); - mtx.vout.push_back(MakeMarmaraCC1of2voutOpret(change, createtxidPk, opret)); // NOTE: change will be rejected by the current validation code - } - rawtx = FinalizeCCTx(0, cp, mtx, minerpk, txfee, MarmaraEncodeLoopSettlementOpret(true, loopData.createtxid, loopData.pk, 0)); - if (rawtx.empty()) { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "could not finalize CC Tx")); - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "bad settlement mtx=" << HexStr(E_MARSHAL(ss << mtx)) << std::endl); - } - else { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", rawtx)); - settlementTx = mtx; - } - return(result); + if ( myGetTransaction(creditloop[i],tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + if ( (funcid= MarmaraDecodeLoopOpret(tx.vout[numvouts-1].scriptPubKey,createtxid,pk,amount,matures,currency)) != 0 ) + { + GetCCaddress1of2(cp,coinaddr,Marmarapk,pk); + if ( (inputsum= AddMarmarainputs(mtx,pubkeys,coinaddr,remaining,MARMARA_VINS)) >= remaining ) + { + change = (inputsum - remaining); + mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if ( change > txfee ) + mtx.vout.push_back(MakeCC1of2vout(EVAL_MARMARA,change,Marmarapk,pk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,MarmaraLoopOpret('S',createtxid,mypk,0,refmatures,currency),pubkeys); + result.push_back(Pair("result",(char *)"success")); + result.push_back(Pair("hex",rawtx)); + return(result); + } else remaining -= inputsum; + if ( mtx.vin.size() >= CC_MAXVINS - MARMARA_VINS ) + break; + } else fprintf(stderr,"null funcid for creditloop[%d]\n",i); + } else fprintf(stderr,"couldnt get creditloop[%d]\n",i); } - else if (lclAmount < loopData.amount) + if ( refamount - remaining > 2*txfee ) { - int64_t remaining = loopData.amount - lclAmount; - mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(CCtxidaddr_tweak(NULL, loopData.createtxid))) << OP_CHECKSIG)); // failure marker - - // TODO: seems this was supposed that txfee should been taken from 1of2 address? - //if (refamount - remaining > 3 * txfee) - // mtx.vout.push_back(CTxOut(refamount - remaining - 2 * txfee, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(CTxOut(loopData.amount - remaining - txfee, CScript() << ParseHex(HexStr(loopData.pk)) << OP_CHECKSIG)); - - rawtx = FinalizeCCTx(0, cp, mtx, minerpk, txfee, MarmaraEncodeLoopSettlementOpret(false, loopData.createtxid, loopData.pk, -remaining)); //some remainder left - if (rawtx.empty()) { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "couldnt finalize CCtx")); - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "FinalizeCCTx bad mtx=" << HexStr(E_MARSHAL(ss << mtx)) << std::endl); - } - else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "insufficient funds")); - result.push_back(Pair("hex", rawtx)); - result.push_back(Pair("remaining", ValueFromAmount(remaining))); - } + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,createtxid))) << OP_CHECKSIG)); // failure marker + if ( refamount-remaining > 3*txfee ) + mtx.vout.push_back(CTxOut(refamount-remaining-2*txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,MarmaraLoopOpret('D',createtxid,mypk,-remaining,refmatures,currency),pubkeys); + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"insufficient funds")); + result.push_back(Pair("hex",rawtx)); + result.push_back(Pair("remaining",ValueFromAmount(remaining))); } else { // jl777: maybe fund a txfee to report no funds avail - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "no funds available at all")); + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"no funds available at all")); } - //} - /*else - { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "this node does not have the baton")); - result.push_back(Pair("myCCaddr", myCCaddr)); - result.push_back(Pair("batonCCaddr", batonCCaddr)); - }*/ } else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "couldnt get batontxid opret")); + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"this node does not have the baton")); + result.push_back(Pair("myCCaddr",myCCaddr)); + result.push_back(Pair("batonCCaddr",batonCCaddr)); } } else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "couldnt find batontxid")); + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"couldnt get batontxid opret")); } } else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "couldnt get credit loop creation data")); + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"couldnt find batontxid")); } } else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "couldnt get creditloop for the baton")); + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"couldnt get creditloop")); } return(result); } -// enums credit loops (for the pk or all if null pk passed) -// returns pending and closed txids (only for MARMARA_MARKER_VOUT) -// for pending loops calls 'callback' with params batontxid and mature height (or -1 if the loop is closed) -template -static int32_t enum_credit_loops(int32_t nVoutMarker, int64_t &totalopen, std::vector &issuances, int64_t &totalclosed, std::vector &closed, struct CCcontract_info *cp, int32_t firstheight, int32_t lastheight, int64_t minamount, int64_t maxamount, CPubKey refpk, std::string refcurrency, T callback/*void (*callback)(uint256 batontxid, int32_t matures)*/) +int32_t MarmaraGetCreditloops(int64_t &totalamount,std::vector &issuances,int64_t &totalclosed,std::vector &closed,struct CCcontract_info *cp,int32_t firstheight,int32_t lastheight,int64_t minamount,int64_t maxamount,CPubKey refpk,std::string refcurrency) { - char marmaraaddr[KOMODO_ADDRESS_BUFSIZE]; - int32_t n = 0; + char coinaddr[64]; CPubKey Marmarapk,senderpk; int64_t amount; uint256 createtxid,txid,hashBlock; CTransaction tx; int32_t numvouts,vout,matures,n=0; std::string currency; std::vector > unspentOutputs; - CPubKey Marmarapk = GetUnspendable(cp, 0); - GetCCaddress(cp, marmaraaddr, Marmarapk); - SetCCunspents(unspentOutputs, marmaraaddr, true); - + Marmarapk = GetUnspendable(cp,0); + GetCCaddress(cp,coinaddr,Marmarapk); + SetCCunspents(unspentOutputs,coinaddr,true); // do all txid, conditional on spent/unspent - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "check on marmara addr=" << marmaraaddr << std::endl); - for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) + //fprintf(stderr,"check coinaddr.(%s)\n",coinaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - CTransaction issuancetx; - uint256 hashBlock; - uint256 issuancetxid = it->first.txhash; - int32_t vout = (int32_t)it->first.index; - - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "checking tx as marker on marmara addr txid=" << issuancetxid.GetHex() << " vout=" << vout << std::endl); - // enum creditloop markers: - if (vout == nVoutMarker) + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //fprintf(stderr,"txid.%s/v%d\n",txid.GetHex().c_str(),vout); + if ( vout == 1 && myGetTransaction(txid,tx,hashBlock) != 0 ) { - if (myGetTransaction(issuancetxid, issuancetx, hashBlock) && !hashBlock.IsNull()) + if ( tx.IsCoinBase() == 0 && (numvouts= tx.vout.size()) > 2 && tx.vout[numvouts - 1].nValue == 0 ) { - if (!issuancetx.IsCoinBase() && issuancetx.vout.size() > 2 && issuancetx.vout.back().nValue == 0 /*has opreturn?*/) + if ( MarmaraDecodeLoopOpret(tx.vout[numvouts-1].scriptPubKey,createtxid,senderpk,amount,matures,currency) == 'I' ) { - struct SMarmaraCreditLoopOpret loopData; - if (MarmaraDecodeLoopOpret(issuancetx.vout.back().scriptPubKey, loopData) == MARMARA_ISSUE) + n++; + if ( currency == refcurrency && matures >= firstheight && matures <= lastheight && amount >= minamount && amount <= maxamount && (refpk.size() == 0 || senderpk == refpk) ) { - if (get_loop_creation_data(loopData.createtxid, loopData) >= 0) - { - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "found issuance tx txid=" << issuancetxid.GetHex() << std::endl); - n++; - //assert(!loopData.currency.empty()); - //assert(loopData.pk.size() != 0); - if (loopData.currency == refcurrency && loopData.matures >= firstheight && loopData.matures <= lastheight && loopData.amount >= minamount && loopData.amount <= maxamount && (refpk.size() == 0 || loopData.pk == refpk)) - { - std::vector creditloop; - uint256 settletxid, batontxid; - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "issuance tx is filtered, txid=" << issuancetxid.GetHex() << std::endl); - - if (get_settlement_txid(settletxid, issuancetxid) == 0) - { - CTransaction settletx; - uint256 hashBlock; - uint8_t funcid; - - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "found settle tx for issueancetxid=" << issuancetxid.GetHex() << std::endl); - - if (myGetTransaction(settletxid, settletx, hashBlock) && !hashBlock.IsNull() && settletx.vout.size() > 1 && - (funcid = MarmaraDecodeLoopOpret(settletx.vout.back().scriptPubKey, loopData)) != 0) - { - closed.push_back(issuancetxid); - totalclosed += loopData.amount; - } - else - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "could not get or decode settletx=" << settletxid.GetHex() << " (tx could be in mempool)" << std::endl); - } - else if (MarmaraGetbatontxid(creditloop, batontxid, issuancetxid) > 0) - { - CTransaction batontx; - uint256 hashBlock; - uint8_t funcid; - - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "found baton tx for issueancetxid=" << issuancetxid.GetHex() << std::endl); - - if (myGetTransaction(batontxid, batontx, hashBlock) && !hashBlock.IsNull() && batontx.vout.size() > 1 && - (funcid = MarmaraDecodeLoopOpret(batontx.vout.back().scriptPubKey, loopData)) != 0) - { - issuances.push_back(issuancetxid); - totalopen += loopData.amount; - callback(batontxid, loopData.matures); - } - else - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "could not get or decode batontx=" << batontxid.GetHex() << " (baton could be in mempool)" << std::endl); - } - else - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "error finding baton for issuance txid=" << issuancetxid.GetHex() << " (tx could be in mempool)" << std::endl); - } - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "error load create tx for createtxid=" << loopData.createtxid.GetHex() << std::endl); + issuances.push_back(txid); + totalamount += amount; } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "incorrect funcid for issuancetxid=" << issuancetxid.GetHex() << std::endl); } } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "cant get tx on marmara marker addr (is in mempool=" << hashBlock.IsNull() << ") txid=" << issuancetxid.GetHex() << std::endl); - } + } else fprintf(stderr,"error getting tx\n"); } return(n); } -// adds to the passed vector the settlement transactions for all matured loops -// called by the miner -// note that several or even all transactions might not fit into the current block, in this case they will be added on the next new block creation -// TODO: provide reserved space in the created block for at least some settlement transactions -void MarmaraRunAutoSettlement(int32_t height, std::vector & settlementTransactions) -{ - int64_t totalopen, totalclosed; - std::vector issuances, closed; - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - std::string funcname = __func__; - - int32_t firstheight = 0, lastheight = (1 << 30); - int64_t minamount = 0, maxamount = (1LL << 60); - - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "starting enum open batons" << std::endl); - enum_credit_loops(MARMARA_OPENCLOSE_VOUT, totalopen, issuances, totalclosed, closed, cp, firstheight, lastheight, minamount, maxamount, CPubKey(), MARMARA_CURRENCY, [&](uint256 batontxid, int32_t matures) - { - CTransaction settlementtx; - //TODO: temp UniValue result legacy code, change to remove UniValue - - if (chainActive.LastTip()->GetHeight() >= matures) //check height if matured - { - LOGSTREAM("marmara", CCLOG_DEBUG2, stream << funcname << " " << "miner calling settlement for batontxid=" << batontxid.GetHex() << std::endl); - - UniValue result = MarmaraSettlement(0, batontxid, settlementtx); - if (result["result"].getValStr() == "success") { - LOGSTREAM("marmara", CCLOG_INFO, stream << funcname << " " << "miner created settlement tx=" << settlementtx.GetHash().GetHex() << ", for batontxid=" << batontxid.GetHex() << std::endl); - settlementTransactions.push_back(settlementtx); - } - else { - LOGSTREAM("marmara", CCLOG_ERROR, stream << funcname << " " << "error=" << result["error"].getValStr() << " in settlement for batontxid=" << batontxid.GetHex() << std::endl); - } - } - }); -} - -// create request tx for issuing or transfer baton (cheque) -// the first call makes the credit loop creation tx -// txid of returned tx is requesttxid -UniValue MarmaraReceive(const CPubKey &remotepk, int64_t txfee, const CPubKey &senderpk, int64_t amount, const std::string ¤cy, int32_t matures, int32_t avalcount, uint256 batontxid, bool automaticflag) +UniValue MarmaraReceive(uint64_t txfee,CPubKey senderpk,int64_t amount,std::string currency,int32_t matures,uint256 batontxid,bool automaticflag) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp, C; - int64_t requestFee; - std::string rawtx; - - cp = CCinit(&C, EVAL_MARMARA); - if (txfee == 0) + UniValue result(UniValue::VOBJ); CPubKey mypk; struct CCcontract_info *cp,C; std::string rawtx; char *errorstr=0; uint256 createtxid; int64_t batonamount; int32_t needbaton = 0; + cp = CCinit(&C,EVAL_MARMARA); + if ( txfee == 0 ) txfee = 10000; - - if (automaticflag != 0 && (matures & 1) == 0) + if ( automaticflag != 0 && (matures & 1) == 0 ) matures++; - else if (automaticflag == 0 && (matures & 1) != 0) + else if ( automaticflag == 0 && (matures & 1) != 0 ) matures++; - - CPubKey mypk; - bool isRemote = IS_REMOTE(remotepk); - if (isRemote) - mypk = remotepk; - else - mypk = pubkey2pk(Mypubkey()); - uint256 createtxid = zeroid; - const char *errorstr = NULL; - - if (batontxid == zeroid) - { - // first time checking parameters - if (currency != MARMARA_CURRENCY) - errorstr = "for now, only MARMARA loops are supported"; - else if (amount <= txfee) - errorstr = "amount must be for more than txfee"; - else if (matures <= chainActive.LastTip()->GetHeight()) - errorstr = "it must mature in the future"; - else if (mypk == senderpk) - errorstr = "cannot request credit from self"; - } - else - { - if (get_create_txid(createtxid, batontxid) < 0) - errorstr = "cant get createtxid from batontxid"; - } - - if (createtxid != zeroid) - { - // check original cheque params: - CTransaction looptx; - uint256 hashBlock; - struct SMarmaraCreditLoopOpret loopData; - - if (get_loop_creation_data(createtxid, loopData) < 0) - errorstr = "cannot get loop creation data"; - else if (!myGetTransaction(batontxid, looptx, hashBlock) || - hashBlock.IsNull() || // not in mempool - looptx.vout.size() < 1 || - MarmaraDecodeLoopOpret(looptx.vout.back().scriptPubKey, loopData) == 0) - { - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "cant get looptx.GetHash()=" << looptx.GetHash().GetHex() << " looptx.vout.size()=" << looptx.vout.size() << std::endl); - errorstr = "cant load previous loop tx or tx in mempool or cant decode tx opreturn data"; - } - else if (senderpk != loopData.pk) - errorstr = "current baton holder does not match the requested sender pk"; - else if (loopData.matures <= chainActive.LastTip()->GetHeight()) - errorstr = "credit loop must mature in the future"; - } - - if (errorstr == NULL) - { - if (batontxid != zeroid) - requestFee = MARMARA_REQUESTTX_AMOUNT; - else - requestFee = MARMARA_CREATETX_AMOUNT; // fee value 20000 for easy identification (?) - if (AddNormalinputs(mtx, mypk, requestFee + txfee, 1, isRemote) > 0) - { - CScript opret; - - mtx.vout.push_back(MakeCC1vout(EVAL_MARMARA, requestFee, senderpk)); - if (batontxid.IsNull()) - opret = MarmaraEncodeLoopCreateOpret(senderpk, amount, matures, currency); - else - opret = MarmaraEncodeLoopRequestOpret(createtxid, senderpk); - - rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, opret); - if (rawtx.size() == 0) - errorstr = "couldnt finalize CCtx"; - } - else - errorstr = "dont have enough normal inputs for requestfee and txfee"; - } - if (rawtx.size() == 0 || errorstr != 0) - { - result.push_back(Pair("result", "error")); - if (errorstr != 0) - result.push_back(Pair("error", errorstr)); - } - else - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", rawtx)); - result.push_back(Pair("funcid", "R")); - result.push_back(Pair("createtxid", createtxid.GetHex())); - if (batontxid != zeroid) - result.push_back(Pair("batontxid", batontxid.GetHex())); - result.push_back(Pair("senderpk", HexStr(senderpk))); - if (batontxid == zeroid) { - result.push_back(Pair("amount", ValueFromAmount(amount))); - result.push_back(Pair("matures", static_cast(matures))); - result.push_back(Pair("currency", currency)); - } - } - return(result); -} - - -static int32_t redistribute_lcl_remainder(CMutableTransaction &mtx, struct CCcontract_info *cp, const std::vector &creditloop, uint256 batontxid, CAmount amountToDistribute) -{ - CPubKey Marmarapk; - int32_t endorsersNumber = creditloop.size(); // number of endorsers, 0 is createtxid, last is holder - CAmount inputsum, change; - std::vector endorserPubkeys; - CTransaction createtx; - uint256 hashBlock, dummytxid; - uint256 createtxid = creditloop[0]; - struct SMarmaraCreditLoopOpret loopData; - - uint8_t marmarapriv[32]; - Marmarapk = GetUnspendable(cp, marmarapriv); - - if (endorsersNumber < 1) // nobody to return to - return 0; - - if (myGetTransaction(createtxid, createtx, hashBlock) && createtx.vout.size() > 1 && - MarmaraDecodeLoopOpret(createtx.vout.back().scriptPubKey, loopData) != 0) // get amount value - { - char lockInLoop1of2addr[KOMODO_ADDRESS_BUFSIZE]; - CPubKey createtxidPk = CCtxidaddr_tweak(NULL, createtxid); - GetCCaddress1of2(cp, lockInLoop1of2addr, Marmarapk, createtxidPk); // 1of2 lock-in-loop address - - // add locked-in-loop utxos: - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "calling AddMarmaraCCInputs for lock-in-loop addr=" << lockInLoop1of2addr << " adding as possible as amount=" << loopData.amount << std::endl); - if ((inputsum = AddMarmaraCCInputs(IsMarmaraLockedInLoopVout, mtx, endorserPubkeys, lockInLoop1of2addr, loopData.amount, MARMARA_VINS)) >= loopData.amount / endorsersNumber) - { - if (mtx.vin.size() >= CC_MAXVINS) {// total vin number limit - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "too many vins!" << std::endl); - return -1; - } - - if (endorserPubkeys.size() != endorsersNumber) { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << " internal error not matched endorserPubkeys.size()=" << endorserPubkeys.size() << " endorsersNumber=" << endorsersNumber << " line=" << __LINE__ << std::endl); - return -1; - } - - CAmount amountReturned = 0; - CAmount amountToPk = amountToDistribute / endorsersNumber; - - //for (int32_t i = 1; i < creditloop.size() + 1; i ++) //iterate through all issuers/endorsers, skip i=0 which is 1st receiver tx, n + 1 is batontxid - for (const auto &endorserPk : endorserPubkeys) - { - mtx.vout.push_back(CTxOut(amountToPk, CScript() << ParseHex(HexStr(endorserPk)) << OP_CHECKSIG)); // coins returned to each previous issuer normal output - amountReturned += amountToPk; - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << " sending normal amount=" << amountToPk << " to pk=" << HexStr(endorserPk) << std::endl); - } - change = (inputsum - amountReturned); - - // return change to the lock-in-loop fund, distribute for pubkeys: - if (change > 0) - { - /* uncomment if the same check above is removed - if (endorserPubkeys.size() != endorsersNumber) { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << " internal error not matched endorsersPubkeys.size()=" << endorserPubkeys.size() << " endorsersNumber=" << endorsersNumber << " line=" << __LINE__ << std::endl); - return -1; - } */ - for (const auto &pk : endorserPubkeys) - { - // each LCL utxo is marked with the pubkey who owns this part of the loop amount - // So for staking only those LCL utxo are picked up that are marked with the current node's pubkey - CScript opret = MarmaraEncodeLoopCCVoutOpret(createtxid, pk); // add mypk to vout to identify who has locked coins in the credit loop - mtx.vout.push_back(MakeMarmaraCC1of2voutOpret(change / endorserPubkeys.size(), createtxidPk, opret)); // TODO: losing remainder? - - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "distributing to loop change/pubkeys.size()=" << change / endorserPubkeys.size() << " cc opret pk=" << HexStr(pk) << std::endl); - } - } - - CC *lockInLoop1of2cond = MakeCCcond1of2(EVAL_MARMARA, Marmarapk, createtxidPk); - CCAddVintxCond(cp, lockInLoop1of2cond, marmarapriv); //add probe condition to spend from the lock-in-loop address - cc_free(lockInLoop1of2cond); - - } - else { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "couldnt get locked-in-loop amount to return to endorsers" << std::endl); - return -1; - } - } - else { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "could not load createtx" << std::endl); - return -1; - } - return 0; -} - - -// issue or transfer coins to the next receiver -UniValue MarmaraIssue(const CPubKey &remotepk, int64_t txfee, uint8_t funcid, const CPubKey &receiverpk, const struct SMarmaraOptParams &optParams, uint256 requesttxid, uint256 batontxid) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); - std::string rawtx; - std::string errorStr; - uint256 createtxid; - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - - if (txfee == 0) - txfee = 10000; - - // make sure less than maxlength (?) - - CPubKey Marmarapk = GetUnspendable(cp, NULL); - CPubKey mypk; - bool isRemote = IS_REMOTE(remotepk); - if (isRemote) - mypk = remotepk; - else - mypk = pubkey2pk(Mypubkey()); - - if (mypk == receiverpk) - errorStr = "cannot send baton to self"; // check it here - else if (get_create_txid(createtxid, requesttxid) < 0) - errorStr = "can't get createtxid from requesttxid (request tx could be in mempool)"; - else if (check_request_tx(requesttxid, receiverpk, funcid, errorStr)) - { - struct SMarmaraCreditLoopOpret loopData; - - if (get_loop_creation_data(createtxid, loopData) >= 0) - { - uint256 dummytxid; - std::vector creditloop; - int32_t endorsersNumber = MarmaraGetbatontxid(creditloop, dummytxid, requesttxid); - - if (endorsersNumber >= 0 && endorsersNumber < MARMARA_MAXENDORSERS) - { - char activated1of2addr[KOMODO_ADDRESS_BUFSIZE]; - int64_t inputsum; - std::vector pubkeys; - int64_t amountToLock = (endorsersNumber > 0 ? loopData.amount / (endorsersNumber + 1) : loopData.amount); // include new endorser - - GetCCaddress1of2(cp, activated1of2addr, Marmarapk, mypk); // 1of2 address where the activated endorser's money is locked - - LOGSTREAMFN("marmara", CCLOG_DEBUG2, stream << "calling AddMarmaraCCInputs for activated addr=" << activated1of2addr << " needs activated amount to lock-in-loop=" << amountToLock << std::endl); - if ((inputsum = AddMarmaraCCInputs(IsMarmaraActivatedVout, mtx, pubkeys, activated1of2addr, amountToLock, MARMARA_VINS)) >= amountToLock) // add 1/n remainder from the locked fund - { - mtx.vin.push_back(CTxIn(requesttxid, MARMARA_REQUEST_VOUT, CScript())); // spend the request tx baton, will add 20000 for marmaraissue or 1*txfee for marmaratransfer - if (funcid == MARMARA_TRANSFER) - mtx.vin.push_back(CTxIn(batontxid, MARMARA_BATON_VOUT, CScript())); // for marmaratransfer spend the previous baton (+ 1*txfee for marmaratransfer) - - if (funcid == MARMARA_TRANSFER || AddNormalinputs(mtx, mypk, txfee + MARMARA_LOOP_MARKER_AMOUNT, 4, isRemote) > 0) // add two more txfee for marmaraissue - { - mtx.vout.push_back(MakeCC1vout(EVAL_MARMARA, MARMARA_LOOP_MARKER_AMOUNT, receiverpk)); // vout0 is transfer of baton to the next receiver (-txfee for marmaraissue and marmaratransfer) - if (funcid == MARMARA_ISSUE) - mtx.vout.push_back(MakeCC1vout(EVAL_MARMARA, MARMARA_LOOP_MARKER_AMOUNT, Marmarapk)); // vout1 is marker in issuance tx to list all loops - - // lock 1/N amount in loop - CPubKey createtxidPk = CCtxidaddr_tweak(NULL, createtxid); - - // add cc lock-in-loop opret - // mark opret with my pk to indicate whose vout it is (to add it as mypk staking utxo) - CScript opret = MarmaraEncodeLoopCCVoutOpret(createtxid, mypk); - // add cc opret with mypk to cc vout - mtx.vout.push_back(MakeMarmaraCC1of2voutOpret(amountToLock, createtxidPk, opret)); //vout2 is issued amount - - if (funcid == MARMARA_ISSUE) - mtx.vout.push_back(MakeCC1vout(EVAL_MARMARA, MARMARA_LOOP_MARKER_AMOUNT, Marmarapk)); // vout3 is open/close marker in issuance tx - - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "sending to loop amount=" << amountToLock << " marked with mypk=" << HexStr(mypk) << std::endl); - - // return change to mypk activated address: - int64_t change = (inputsum - amountToLock); - if (change > 0) - { - int32_t height = komodo_nextheight(); - if ((height & 1) != 0) // make height even as only even height is considered for staking (TODO: strange) - height++; - CScript opret = MarmaraCoinbaseOpret(MARMARA_ACTIVATED, height, mypk); - // add coinbase opret to ccvout for the change - mtx.vout.push_back(MakeMarmaraCC1of2voutOpret(change, mypk, opret)); // adding MarmaraCoinbase cc vout 'opret' for change - } - - if (endorsersNumber < 1 || redistribute_lcl_remainder(mtx, cp, creditloop, batontxid, amountToLock) >= 0) // if there are issuers already then distribute and return amount / n value - { - CC* activated1of2cond = MakeCCcond1of2(EVAL_MARMARA, Marmarapk, mypk); // create vintx probe 1of2 cond - CCAddVintxCond(cp, activated1of2cond); // add the probe to cp, it is copied and we can cc_free it - cc_free(activated1of2cond); - - CScript opret; - if (funcid == MARMARA_ISSUE) - opret = MarmaraEncodeLoopIssuerOpret(createtxid, receiverpk, optParams.autoSettlement, optParams.autoInsurance, optParams.avalCount, optParams.disputeExpiresOffset, optParams.escrowOn, optParams.blockageAmount); - else - opret = MarmaraEncodeLoopTransferOpret(createtxid, receiverpk, optParams.avalCount); - - rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, opret); - - if (rawtx.size() == 0) { - errorStr = "couldnt finalize tx"; - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "couldnt finalize, bad mtx=" << HexStr(E_MARSHAL(ss << mtx)) << std::endl); - } - } - else - errorStr = "could not return locked in loop funds to endorsers"; - } - else - errorStr = "dont have enough normal inputs for txfee"; - } - else - errorStr = "dont have enough locked inputs for amount"; - } - else - { - if (endorsersNumber >= MARMARA_MAXENDORSERS) - errorStr = "too many endorsers"; - else - errorStr = "incorrect requesttxid"; - - } - } - else - { - errorStr = "cannot get loop creation data"; - } - } - if (!errorStr.empty()) - { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", errorStr)); + mypk = pubkey2pk(Mypubkey()); + memset(&createtxid,0,sizeof(createtxid)); + if ( batontxid != zeroid && MarmaraGetcreatetxid(createtxid,batontxid) < 0 ) + errorstr = (char *)"cant get createtxid from batontxid"; + else if ( currency != "MARMARA" ) + errorstr = (char *)"for now, only MARMARA loops are supported"; + else if ( amount <= txfee ) + errorstr = (char *)"amount must be for more than txfee"; + else if ( matures <= chainActive.LastTip()->GetHeight() ) + errorstr = (char *)"it must mature in the future"; + if ( errorstr == 0 ) + { + if ( batontxid != zeroid ) + batonamount = txfee; + else batonamount = 2*txfee; + if ( AddNormalinputs(mtx,mypk,batonamount + txfee,1) > 0 ) + { + errorstr = (char *)"couldnt finalize CCtx"; + mtx.vout.push_back(MakeCC1vout(EVAL_MARMARA,batonamount,senderpk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,MarmaraLoopOpret('R',createtxid,senderpk,amount,matures,currency)); + if ( rawtx.size() > 0 ) + errorstr = 0; + } else errorstr = (char *)"dont have enough normal inputs for 2*txfee"; + } + if ( rawtx.size() == 0 || errorstr != 0 ) + { + result.push_back(Pair("result","error")); + if ( errorstr != 0 ) + result.push_back(Pair("error",errorstr)); } else { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", rawtx)); - char sfuncid[2]; - sfuncid[0] = funcid; - sfuncid[1] = '\0'; - result.push_back(Pair("funcid", sfuncid)); - result.push_back(Pair("createtxid", createtxid.GetHex())); - result.push_back(Pair("requesttxid", requesttxid.GetHex())); - if (funcid == MARMARA_TRANSFER) - result.push_back(Pair("batontxid", batontxid.GetHex())); - result.push_back(Pair("receiverpk", HexStr(receiverpk))); -// result.push_back(Pair("amount", ValueFromAmount(amount))); -// result.push_back(Pair("matures", static_cast(matures))); -// result.push_back(Pair("currency", currency)); + result.push_back(Pair("result",(char *)"success")); + result.push_back(Pair("hex",rawtx)); + result.push_back(Pair("funcid","R")); + result.push_back(Pair("createtxid",createtxid.GetHex())); + if ( batontxid != zeroid ) + result.push_back(Pair("batontxid",batontxid.GetHex())); + result.push_back(Pair("senderpk",HexStr(senderpk))); + result.push_back(Pair("amount",ValueFromAmount(amount))); + result.push_back(Pair("matures",matures)); + result.push_back(Pair("currency",currency)); } return(result); } -UniValue MarmaraCreditloop(const CPubKey & remotepk, uint256 txid) -{ - UniValue result(UniValue::VOBJ), a(UniValue::VARR); - std::vector creditloop; - uint256 batontxid, hashBlock; - uint8_t funcid; - int32_t numerrs = 0, n; - CTransaction lasttx; - struct CCcontract_info *cp, C; - struct SMarmaraCreditLoopOpret loopData; - bool isSettledOk = false; - - CPubKey mypk; - if (IS_REMOTE(remotepk)) - mypk = remotepk; - else - mypk = pubkey2pk(Mypubkey()); - - cp = CCinit(&C, EVAL_MARMARA); - if ((n = MarmaraGetbatontxid(creditloop, batontxid, txid)) > 0) - { - if (get_loop_creation_data(creditloop[0], loopData) == 0) - { - uint256 issuetxid, settletxid, lasttxid; - int32_t vini, height; - - if (n > 1) - issuetxid = creditloop[1]; - else - issuetxid = batontxid; - - std::vector looptxids (creditloop.begin(), creditloop.end()); - - if (get_settlement_txid(settletxid, issuetxid) == 0) - { - // loop is closed - last tx is the settle tx - lasttxid = settletxid; - looptxids.push_back(batontxid); // add baton to to add its info to the result too - } - else - { - // loop is not closed - last tx is the baton - lasttxid = batontxid; - } +UniValue MarmaraIssue(uint64_t txfee,uint8_t funcid,CPubKey receiverpk,int64_t amount,std::string currency,int32_t matures,uint256 approvaltxid,uint256 batontxid) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); CPubKey mypk,Marmarapk; struct CCcontract_info *cp,C; std::string rawtx; uint256 createtxid; char *errorstr=0; + cp = CCinit(&C,EVAL_MARMARA); + if ( txfee == 0 ) + txfee = 10000; + // make sure less than maxlength + Marmarapk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + if ( MarmaraGetcreatetxid(createtxid,approvaltxid) < 0 ) + errorstr = (char *)"cant get createtxid from approvaltxid"; + else if ( currency != "MARMARA" ) + errorstr = (char *)"for now, only MARMARA loops are supported"; + else if ( amount <= txfee ) + errorstr = (char *)"amount must be for more than txfee"; + else if ( matures <= chainActive.LastTip()->GetHeight() ) + errorstr = (char *)"it must mature in the future"; + if ( errorstr == 0 ) + { + mtx.vin.push_back(CTxIn(approvaltxid,0,CScript())); + if ( funcid == 'T' ) + mtx.vin.push_back(CTxIn(batontxid,0,CScript())); + if ( funcid == 'I' || AddNormalinputs(mtx,mypk,txfee,1) > 0 ) + { + errorstr = (char *)"couldnt finalize CCtx"; + mtx.vout.push_back(MakeCC1vout(EVAL_MARMARA,txfee,receiverpk)); + if ( funcid == 'I' ) + mtx.vout.push_back(MakeCC1vout(EVAL_MARMARA,txfee,Marmarapk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,MarmaraLoopOpret(funcid,createtxid,receiverpk,amount,matures,currency)); + if ( rawtx.size() > 0 ) + errorstr = 0; + } else errorstr = (char *)"dont have enough normal inputs for 2*txfee"; + } + if ( rawtx.size() == 0 || errorstr != 0 ) + { + result.push_back(Pair("result","error")); + if ( errorstr != 0 ) + result.push_back(Pair("error",errorstr)); + } + else + { + result.push_back(Pair("result",(char *)"success")); + result.push_back(Pair("hex",rawtx)); + char str[2]; str[0] = funcid, str[1] = 0; + result.push_back(Pair("funcid",str)); + result.push_back(Pair("createtxid",createtxid.GetHex())); + result.push_back(Pair("approvaltxid",approvaltxid.GetHex())); + if ( funcid == 'T' ) + result.push_back(Pair("batontxid",batontxid.GetHex())); + result.push_back(Pair("receiverpk",HexStr(receiverpk))); + result.push_back(Pair("amount",ValueFromAmount(amount))); + result.push_back(Pair("matures",matures)); + result.push_back(Pair("currency",currency)); + } + return(result); +} - // add last tx info - if (myGetTransaction(lasttxid, lasttx, hashBlock) && lasttx.vout.size() > 1) +UniValue MarmaraCreditloop(uint256 txid) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::vector creditloop; uint256 batontxid,createtxid,refcreatetxid,hashBlock; uint8_t funcid; int32_t numerrs=0,i,n,numvouts,matures,refmatures; int64_t amount,refamount; CPubKey pk; std::string currency,refcurrency; CTransaction tx; char coinaddr[64],myCCaddr[64],destaddr[64],batonCCaddr[64],str[2]; struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_MARMARA); + if ( (n= MarmaraGetbatontxid(creditloop,batontxid,txid)) > 0 ) + { + if ( myGetTransaction(batontxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + result.push_back(Pair("result",(char *)"success")); + Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(Mypubkey())) << OP_CHECKSIG); + result.push_back(Pair("myaddress",coinaddr)); + GetCCaddress(cp,myCCaddr,Mypubkey()); + result.push_back(Pair("myCCaddress",myCCaddr)); + if ( (funcid= MarmaraDecodeLoopOpret(tx.vout[numvouts-1].scriptPubKey,refcreatetxid,pk,refamount,refmatures,refcurrency)) != 0 ) { - char normaladdr[KOMODO_ADDRESS_BUFSIZE], myCCaddr[KOMODO_ADDRESS_BUFSIZE], vout0addr[KOMODO_ADDRESS_BUFSIZE], batonCCaddr[KOMODO_ADDRESS_BUFSIZE]; - vuint8_t vmypk(mypk.begin(), mypk.end()); - - result.push_back(Pair("result", "success")); - Getscriptaddress(normaladdr, CScript() << ParseHex(HexStr(vmypk)) << OP_CHECKSIG); - result.push_back(Pair("myNormalAddress", normaladdr)); - GetCCaddress(cp, myCCaddr, vmypk); - result.push_back(Pair("myCCaddress", myCCaddr)); - - if ((funcid = MarmaraDecodeLoopOpret(lasttx.vout.back().scriptPubKey, loopData)) != 0) + str[0] = funcid, str[1] = 0; + result.push_back(Pair("funcid",str)); + result.push_back(Pair("currency",refcurrency)); + if ( funcid == 'S' ) { - std::string sfuncid(1, (char)funcid); - result.push_back(Pair("funcid", sfuncid)); - result.push_back(Pair("currency", loopData.currency)); - - if (loopData.createtxid != creditloop[0]) + refcreatetxid = creditloop[0]; + result.push_back(Pair("settlement",batontxid.GetHex())); + result.push_back(Pair("createtxid",refcreatetxid.GetHex())); + result.push_back(Pair("remainder",ValueFromAmount(refamount))); + result.push_back(Pair("settled",refmatures)); + result.push_back(Pair("pubkey",HexStr(pk))); + Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); + result.push_back(Pair("coinaddr",coinaddr)); + result.push_back(Pair("collected",ValueFromAmount(tx.vout[0].nValue))); + Getscriptaddress(destaddr,tx.vout[0].scriptPubKey); + if ( strcmp(coinaddr,destaddr) != 0 ) { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "invalid loopData.createtxid for creditloop[0]=" << creditloop[0].GetHex() << " " << std::endl); - result.push_back(Pair("incorrect-createtxid-in-baton-opret", loopData.createtxid.GetHex())); + result.push_back(Pair("destaddr",destaddr)); numerrs++; } - - if (funcid == MARMARA_SETTLE) //settled okay + refamount = -1; + } + else if ( funcid == 'D' ) + { + refcreatetxid = creditloop[0]; + result.push_back(Pair("settlement",batontxid.GetHex())); + result.push_back(Pair("createtxid",refcreatetxid.GetHex())); + result.push_back(Pair("remainder",ValueFromAmount(refamount))); + result.push_back(Pair("settled",refmatures)); + Getscriptaddress(destaddr,tx.vout[0].scriptPubKey); + result.push_back(Pair("txidaddr",destaddr)); + if ( tx.vout.size() > 1 ) + result.push_back(Pair("collected",ValueFromAmount(tx.vout[1].nValue))); + } + else + { + result.push_back(Pair("batontxid",batontxid.GetHex())); + result.push_back(Pair("createtxid",refcreatetxid.GetHex())); + result.push_back(Pair("amount",ValueFromAmount(refamount))); + result.push_back(Pair("matures",refmatures)); + if ( refcreatetxid != creditloop[0] ) { - //refcreatetxid = creditloop[0]; - result.push_back(Pair("settlement", settletxid.GetHex())); - result.push_back(Pair("createtxid", creditloop[0].GetHex())); - result.push_back(Pair("remainder", ValueFromAmount(loopData.remaining))); - result.push_back(Pair("settled", static_cast(loopData.matures))); - result.push_back(Pair("pubkey", HexStr(loopData.pk))); - Getscriptaddress(normaladdr, CScript() << ParseHex(HexStr(loopData.pk)) << OP_CHECKSIG); - result.push_back(Pair("settledToNormalAddress", normaladdr)); - result.push_back(Pair("collected", ValueFromAmount(lasttx.vout[0].nValue))); - Getscriptaddress(vout0addr, lasttx.vout[0].scriptPubKey); - if (strcmp(normaladdr, vout0addr) != 0) - { - result.push_back(Pair("incorrect-vout0-address-not-matched-pk-in-opret", vout0addr)); - numerrs++; - } - isSettledOk = true; + fprintf(stderr,"invalid refcreatetxid, setting to creditloop[0]\n"); + refcreatetxid = creditloop[0]; + numerrs++; } - else if (funcid == MARMARA_SETTLE_PARTIAL) //settled partially + result.push_back(Pair("batonpk",HexStr(pk))); + Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); + result.push_back(Pair("batonaddr",coinaddr)); + GetCCaddress(cp,batonCCaddr,pk); + result.push_back(Pair("batonCCaddr",batonCCaddr)); + Getscriptaddress(coinaddr,tx.vout[0].scriptPubKey); + if ( strcmp(coinaddr,batonCCaddr) != 0 ) { - //refcreatetxid = creditloop[0]; - result.push_back(Pair("settlement", settletxid.GetHex())); - result.push_back(Pair("createtxid", creditloop[0].GetHex())); - result.push_back(Pair("remainder", ValueFromAmount(loopData.remaining))); - result.push_back(Pair("settled", static_cast(loopData.matures))); - Getscriptaddress(vout0addr, lasttx.vout[0].scriptPubKey); - result.push_back(Pair("txidaddr", vout0addr)); //TODO: why 'txidaddr'? - if (lasttx.vout.size() > 1) - result.push_back(Pair("collected", ValueFromAmount(lasttx.vout[1].nValue))); + result.push_back(Pair("vout0address",coinaddr)); + numerrs++; } - else + if ( strcmp(myCCaddr,coinaddr) == 0 ) + result.push_back(Pair("ismine",1)); + else result.push_back(Pair("ismine",0)); + } + for (i=0; i 1 ) { - result.push_back(Pair("batontxid", batontxid.GetHex())); - result.push_back(Pair("createtxid", creditloop[0].GetHex())); - result.push_back(Pair("amount", ValueFromAmount(loopData.amount))); - result.push_back(Pair("matures", static_cast(loopData.matures))); - result.push_back(Pair("batonpk", HexStr(loopData.pk))); - Getscriptaddress(normaladdr, CScript() << ParseHex(HexStr(loopData.pk)) << OP_CHECKSIG); - result.push_back(Pair("batonaddr", normaladdr)); - GetCCaddress(cp, batonCCaddr, loopData.pk); // baton address - result.push_back(Pair("batonCCaddr", batonCCaddr)); - Getscriptaddress(vout0addr, lasttx.vout[0].scriptPubKey); - if (strcmp(vout0addr, batonCCaddr) != 0) // TODO: how is this possible? + if ( (funcid= MarmaraDecodeLoopOpret(tx.vout[numvouts-1].scriptPubKey,createtxid,pk,amount,matures,currency)) != 0 ) { - result.push_back(Pair("incorrect-vout0-address-not-matched-baton-address", normaladdr)); - numerrs++; + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("txid",creditloop[i].GetHex())); + str[0] = funcid, str[1] = 0; + obj.push_back(Pair("funcid",str)); + if ( funcid == 'R' && createtxid == zeroid ) + { + createtxid = creditloop[i]; + obj.push_back(Pair("issuerpk",HexStr(pk))); + Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); + obj.push_back(Pair("issueraddr",coinaddr)); + GetCCaddress(cp,coinaddr,pk); + obj.push_back(Pair("issuerCCaddr",coinaddr)); + } + else + { + obj.push_back(Pair("receiverpk",HexStr(pk))); + Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); + obj.push_back(Pair("receiveraddr",coinaddr)); + GetCCaddress(cp,coinaddr,pk); + obj.push_back(Pair("receiverCCaddr",coinaddr)); + } + Getscriptaddress(destaddr,tx.vout[0].scriptPubKey); + if ( strcmp(destaddr,coinaddr) != 0 ) + { + obj.push_back(Pair("vout0address",destaddr)); + numerrs++; + } + if ( i == 0 && refamount < 0 ) + { + refamount = amount; + refmatures = matures; + result.push_back(Pair("amount",ValueFromAmount(refamount))); + result.push_back(Pair("matures",refmatures)); + } + if ( createtxid != refcreatetxid || amount != refamount || matures != refmatures || currency != refcurrency ) + { + numerrs++; + obj.push_back(Pair("objerror",(char *)"mismatched createtxid or amount or matures or currency")); + obj.push_back(Pair("createtxid",createtxid.GetHex())); + obj.push_back(Pair("amount",ValueFromAmount(amount))); + obj.push_back(Pair("matures",matures)); + obj.push_back(Pair("currency",currency)); + } + a.push_back(obj); } - - if (strcmp(myCCaddr, /*normaladdr*/batonCCaddr) == 0) // TODO: impossible with normal addr - result.push_back(Pair("ismine", static_cast(1))); - else - result.push_back(Pair("ismine", static_cast(0))); } } - else - { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "couldnt decode last tx opret")); - return result; - } + result.push_back(Pair("n",n)); + result.push_back(Pair("numerrors",numerrs)); + result.push_back(Pair("creditloop",a)); } else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "couldnt load last tx or incorrect last tx")); - return result; - } - - // add locked-in-loop amount: - char lockInLoop1of2addr[KOMODO_ADDRESS_BUFSIZE]; - CPubKey createtxidPk = CCtxidaddr_tweak(NULL, creditloop[0]); - GetCCaddress1of2(cp, lockInLoop1of2addr, GetUnspendable(cp, NULL), createtxidPk); // 1of2 lock-in-loop address - std::vector pubkeys; - CMutableTransaction mtx; - - int64_t amountLockedInLoop = AddMarmaraCCInputs(IsMarmaraLockedInLoopVout, mtx, pubkeys, lockInLoop1of2addr, 0, 0); - result.push_back(Pair("LockedInLoopCCaddr", lockInLoop1of2addr)); - result.push_back(Pair("LockedInLoopAmount", ValueFromAmount(amountLockedInLoop))); // should be 0 if - - // add credit loop data: - for (int32_t i = 0; i < looptxids.size(); i++) - { - if (myGetTransaction(looptxids[i], lasttx, hashBlock) != 0 && lasttx.vout.size() > 1) - { - //uint256 createtxid = zeroid; - if ((funcid = MarmaraDecodeLoopOpret(lasttx.vout.back().scriptPubKey, loopData)) != 0) - { - char normaladdr[KOMODO_ADDRESS_BUFSIZE], ccaddr[KOMODO_ADDRESS_BUFSIZE], vout0addr[KOMODO_ADDRESS_BUFSIZE]; - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("txid", looptxids[i].GetHex())); - std::string sfuncid(1, (char)funcid); - obj.push_back(Pair("funcid", sfuncid)); - if (funcid == MARMARA_REQUEST || funcid == MARMARA_CREATELOOP) - { - //createtxid = looptxids[i]; - obj.push_back(Pair("issuerpk", HexStr(loopData.pk))); - Getscriptaddress(normaladdr, CScript() << ParseHex(HexStr(loopData.pk)) << OP_CHECKSIG); - obj.push_back(Pair("issuerNormalAddress", normaladdr)); - GetCCaddress(cp, ccaddr, loopData.pk); - obj.push_back(Pair("issuerCCAddress", ccaddr)); - } - else - { - obj.push_back(Pair("receiverpk", HexStr(loopData.pk))); - Getscriptaddress(normaladdr, CScript() << ParseHex(HexStr(loopData.pk)) << OP_CHECKSIG); - obj.push_back(Pair("receiverNormalAddress", normaladdr)); - GetCCaddress(cp, ccaddr, loopData.pk); - obj.push_back(Pair("receiverCCAddress", ccaddr)); - } - Getscriptaddress(vout0addr, lasttx.vout[0].scriptPubKey); - if (strcmp(vout0addr, normaladdr) != 0) - { - obj.push_back(Pair("incorrect-vout0address", vout0addr)); - numerrs++; - } - if (i == 0 && isSettledOk) // why isSettledOk checked?.. - { - result.push_back(Pair("amount", ValueFromAmount(loopData.amount))); - result.push_back(Pair("matures", static_cast(loopData.matures))); - } - /* not relevant now as we do not copy params to new oprets - if (createtxid != refcreatetxid || amount != refamount || matures != refmatures || currency != refcurrency) - { - numerrs++; - obj.push_back(Pair("objerror", (char *)"mismatched createtxid or amount or matures or currency")); - obj.push_back(Pair("createtxid", createtxid.GetHex())); - obj.push_back(Pair("amount", ValueFromAmount(amount))); - obj.push_back(Pair("matures", static_cast(matures))); - obj.push_back(Pair("currency", currency)); - } */ - a.push_back(obj); - } - } + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"couldnt get batontxid opret")); } - result.push_back(Pair("n", static_cast(n))); - result.push_back(Pair("numerrors", static_cast(numerrs))); - result.push_back(Pair("creditloop", a)); - - } - else - { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "couldnt get loop creation data")); - } - } - else if (n == 0) - { - // output info of createtx if only createtx exists - if (get_loop_creation_data(txid, loopData) == 0) - { - std::string sfuncid(1, (char)loopData.lastfuncid); - result.push_back(Pair("funcid", sfuncid)); - result.push_back(Pair("currency", loopData.currency)); - result.push_back(Pair("amount", ValueFromAmount(loopData.amount))); - result.push_back(Pair("matures", static_cast(loopData.matures))); - result.push_back(Pair("issuerpk", HexStr(loopData.pk))); - result.push_back(Pair("createtxid", txid.GetHex())); } else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "couldnt get loop creation data")); + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"couldnt find batontxid")); } } else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "couldnt get creditloop")); + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"couldnt get creditloop")); } return(result); } -// collect miner pool rewards (?) -UniValue MarmaraPoolPayout(int64_t txfee, int32_t firstheight, double perc, char *jsonstr) // [[pk0, shares0], [pk1, shares1], ...] +UniValue MarmaraPoolPayout(uint64_t txfee,int32_t firstheight,double perc,char *jsonstr) // [[pk0, shares0], [pk1, shares1], ...] { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ), a(UniValue::VARR); - cJSON *item, *array; std::string rawtx; - int32_t i, n; - uint8_t buf[CPubKey::COMPRESSED_PUBLIC_KEY_SIZE]; - CPubKey Marmarapk, pk, poolpk; - int64_t payout, poolfee = 0, total, totalpayout = 0; - double poolshares, share, shares = 0.; - char *pkstr; - const char *errorstr = 0; - struct CCcontract_info *cp, C; - + UniValue result(UniValue::VOBJ),a(UniValue::VARR); cJSON *item,*array; std::string rawtx; int32_t i,n; uint8_t buf[33]; CPubKey Marmarapk,pk,poolpk; int64_t payout,poolfee=0,total,totalpayout=0; double poolshares,share,shares = 0.; char *pkstr,*errorstr=0; struct CCcontract_info *cp,C; poolpk = pubkey2pk(Mypubkey()); - if (txfee == 0) + if ( txfee == 0 ) txfee = 10000; - cp = CCinit(&C, EVAL_MARMARA); - Marmarapk = GetUnspendable(cp, 0); - if ((array = cJSON_Parse(jsonstr)) != 0 && (n = cJSON_GetArraySize(array)) > 0) + cp = CCinit(&C,EVAL_MARMARA); + Marmarapk = GetUnspendable(cp,0); + if ( (array= cJSON_Parse(jsonstr)) != 0 && (n= cJSON_GetArraySize(array)) > 0 ) { - for (i = 0; i < n; i++) + for (i=0; i, ]"; break; } } - if (errorstr == 0 && shares > SMALLVAL) + if ( errorstr == 0 && shares > SMALLVAL ) { shares += shares * perc; - if ((total = AddMarmaraCoinbases(cp, mtx, firstheight, poolpk, 60)) > 0) + if ( (total= AddMarmaraCoinbases(cp,mtx,firstheight,poolpk,60)) > 0 ) { - for (i = 0; i < n; i++) + for (i=0; i SMALLVAL) + item = jitem(array,i); + if ( (share= jdouble(jitem(item,1),0)) > SMALLVAL ) { payout = (share * (total - txfee)) / shares; - if (payout > 0) + if ( payout > 0 ) { - if ((pkstr = jstr(jitem(item, 0), 0)) != 0 && strlen(pkstr) == (2 * CPubKey::COMPRESSED_PUBLIC_KEY_SIZE)) + if ( (pkstr= jstr(jitem(item,0),0)) != 0 && strlen(pkstr) == 66 ) { UniValue x(UniValue::VOBJ); totalpayout += payout; - decode_hex(buf, CPubKey::COMPRESSED_PUBLIC_KEY_SIZE, pkstr); - mtx.vout.push_back(MakeCC1of2vout(EVAL_MARMARA, payout, Marmarapk, buf2pk(buf))); - x.push_back(Pair(pkstr, (double)payout / COIN)); + decode_hex(buf,33,pkstr); + mtx.vout.push_back(MakeCC1of2vout(EVAL_MARMARA,payout,Marmarapk,buf2pk(buf))); + x.push_back(Pair(pkstr, (double)payout/COIN)); a.push_back(x); } } } } - if (totalpayout > 0 && total > totalpayout - txfee) + if ( totalpayout > 0 && total > totalpayout-txfee ) { poolfee = (total - totalpayout - txfee); - mtx.vout.push_back(MakeCC1of2vout(EVAL_MARMARA, poolfee, Marmarapk, poolpk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_MARMARA,poolfee,Marmarapk,poolpk)); } - rawtx = FinalizeCCTx(0, cp, mtx, poolpk, txfee, MarmaraCoinbaseOpret(MARMARA_POOL, firstheight, poolpk)); - if (rawtx.size() == 0) - errorstr = "couldnt finalize CCtx"; - } - else errorstr = "couldnt find any coinbases to payout"; + rawtx = FinalizeCCTx(0,cp,mtx,poolpk,txfee,MarmaraCoinbaseOpret('P',firstheight,poolpk)); + if ( rawtx.size() == 0 ) + errorstr = (char *)"couldnt finalize CCtx"; + } else errorstr = (char *)"couldnt find any coinbases to payout"; } - else if (errorstr == 0) - errorstr = "no valid shares submitted"; + else if ( errorstr == 0 ) + errorstr = (char *)"no valid shares submitted"; free(array); - } - else errorstr = "couldnt parse poolshares jsonstr"; - if (rawtx.size() == 0 || errorstr != 0) + } else errorstr = (char *)"couldnt parse poolshares jsonstr"; + if ( rawtx.size() == 0 || errorstr != 0 ) { - result.push_back(Pair("result", "error")); - if (errorstr != 0) - result.push_back(Pair("error", errorstr)); + result.push_back(Pair("result","error")); + if ( errorstr != 0 ) + result.push_back(Pair("error",errorstr)); } else { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", rawtx)); - if (totalpayout > 0 && total > totalpayout - txfee) + result.push_back(Pair("result",(char *)"success")); + result.push_back(Pair("hex",rawtx)); + if ( totalpayout > 0 && total > totalpayout-txfee ) { - result.push_back(Pair("firstheight", static_cast(firstheight))); - result.push_back(Pair("lastheight", static_cast(((firstheight / MARMARA_GROUPSIZE) + 1) * MARMARA_GROUPSIZE - 1))); - result.push_back(Pair("total", ValueFromAmount(total))); - result.push_back(Pair("totalpayout", ValueFromAmount(totalpayout))); - result.push_back(Pair("totalshares", shares)); - result.push_back(Pair("poolfee", ValueFromAmount(poolfee))); - result.push_back(Pair("perc", ValueFromAmount((int64_t)(100. * (double)poolfee / totalpayout * COIN)))); - result.push_back(Pair("payouts", a)); + result.push_back(Pair("firstheight",firstheight)); + result.push_back(Pair("lastheight",((firstheight / MARMARA_GROUPSIZE)+1) * MARMARA_GROUPSIZE - 1)); + result.push_back(Pair("total",ValueFromAmount(total))); + result.push_back(Pair("totalpayout",ValueFromAmount(totalpayout))); + result.push_back(Pair("totalshares",shares)); + result.push_back(Pair("poolfee",ValueFromAmount(poolfee))); + result.push_back(Pair("perc",ValueFromAmount((int64_t)(100. * (double)poolfee/totalpayout * COIN)))); + result.push_back(Pair("payouts",a)); } } return(result); @@ -3690,570 +1050,55 @@ UniValue MarmaraPoolPayout(int64_t txfee, int32_t firstheight, double perc, char // get all tx, constrain by vout, issuances[] and closed[] -UniValue MarmaraInfo(const CPubKey &refpk, int32_t firstheight, int32_t lastheight, int64_t minamount, int64_t maxamount, const std::string ¤cyparam) +UniValue MarmaraInfo(CPubKey refpk,int32_t firstheight,int32_t lastheight,int64_t minamount,int64_t maxamount,std::string currency) { - CMutableTransaction mtx; - std::string currency; - std::vector pubkeys; - UniValue result(UniValue::VOBJ), a(UniValue::VARR), b(UniValue::VARR); - int32_t n; int64_t totalclosed = 0, totalamount = 0; - std::vector issuances, closed; - CPubKey Marmarapk; - char mynormaladdr[KOMODO_ADDRESS_BUFSIZE]; - char activated1of2addr[KOMODO_ADDRESS_BUFSIZE]; - char myccaddr[KOMODO_ADDRESS_BUFSIZE]; - vuint8_t vrefpk(refpk.begin(), refpk.end()); - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - - Marmarapk = GetUnspendable(cp, 0); - result.push_back(Pair("result", "success")); + CMutableTransaction mtx; std::vector pubkeys; + UniValue result(UniValue::VOBJ),a(UniValue::VARR),b(UniValue::VARR); int32_t i,n,matches; int64_t totalclosed=0,totalamount=0; std::vector issuances,closed; char coinaddr[64]; + CPubKey Marmarapk; struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_MARMARA); + Marmarapk = GetUnspendable(cp,0); + result.push_back(Pair("result","success")); + Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(Mypubkey())) << OP_CHECKSIG); + result.push_back(Pair("myaddress",coinaddr)); + result.push_back(Pair("normal",ValueFromAmount(CCaddress_balance(coinaddr,0)))); - Getscriptaddress(mynormaladdr, CScript() << ParseHex(HexStr(vrefpk)) << OP_CHECKSIG); - result.push_back(Pair("myNormalAddress", mynormaladdr)); - result.push_back(Pair("myNormalAmount", ValueFromAmount(CCaddress_balance(mynormaladdr, 0)))); - - GetCCaddress1of2(cp, activated1of2addr, Marmarapk, vrefpk); - result.push_back(Pair("myCCActivatedAddress", activated1of2addr)); - result.push_back(Pair("myActivatedAmount", ValueFromAmount(AddMarmaraCCInputs(IsMarmaraActivatedVout, mtx, pubkeys, activated1of2addr, 0, MARMARA_VINS)))); - result.push_back(Pair("myTotalAmountOnActivatedAddress", ValueFromAmount(CCaddress_balance(activated1of2addr, 1)))); - - GetCCaddress(cp, myccaddr, vrefpk); - result.push_back(Pair("myCCAddress", myccaddr)); - result.push_back(Pair("myCCBalance", ValueFromAmount(CCaddress_balance(myccaddr, 1)))); - - // calc lock-in-loops amount for refpk: - CAmount loopAmount = 0; - CAmount totalLoopAmount = 0; - char prevloopaddr[KOMODO_ADDRESS_BUFSIZE] = ""; - UniValue resultloops(UniValue::VARR); - EnumLockedInLoop( - [&](char *loopaddr, const CTransaction & tx, int32_t nvout, CBlockIndex *pindex) // call enumerator with callback - { - if (strcmp(prevloopaddr, loopaddr) != 0) // loop address changed - { - if (prevloopaddr[0] != '\0') // prevloop was - { - UniValue entry(UniValue::VOBJ); - // if new loop then store amount for the prevloop - entry.push_back(Pair("LoopAddress", prevloopaddr)); - entry.push_back(Pair("myAmountLockedInLoop", ValueFromAmount(loopAmount))); - resultloops.push_back(entry); - loopAmount = 0; //reset for the next loop - } - strcpy(prevloopaddr, loopaddr); - } - loopAmount += tx.vout[nvout].nValue; - totalLoopAmount += tx.vout[nvout].nValue; - }, - refpk - ); - - if (prevloopaddr[0] != '\0') { // last loop - UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("LoopAddress", prevloopaddr)); - entry.push_back(Pair("myAmountLockedInLoop", ValueFromAmount(loopAmount))); - resultloops.push_back(entry); - //std::cerr << "lastloop " << " prevloopaddr=" << prevloopaddr << " loopAmount=" << loopAmount << std::endl; - } - result.push_back(Pair("Loops", resultloops)); - result.push_back(Pair("TotalLockedInLoop", ValueFromAmount(totalLoopAmount))); - - if (refpk.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) - result.push_back(Pair("issuer", HexStr(refpk))); - if (currencyparam.empty()) - currency = (char *)MARMARA_CURRENCY; - else - currency = currencyparam; - if (firstheight <= lastheight) + GetCCaddress1of2(cp,coinaddr,Marmarapk,Mypubkey()); + result.push_back(Pair("myCCactivated",coinaddr)); + result.push_back(Pair("activated",ValueFromAmount(CCaddress_balance(coinaddr,1)))); + result.push_back(Pair("activated16",ValueFromAmount(AddMarmarainputs(mtx,pubkeys,coinaddr,0,MARMARA_VINS)))); + + GetCCaddress(cp,coinaddr,Mypubkey()); + result.push_back(Pair("myCCaddress",coinaddr)); + result.push_back(Pair("CCutxos",ValueFromAmount(CCaddress_balance(coinaddr,1)))); + + if ( refpk.size() == 33 ) + result.push_back(Pair("issuer",HexStr(refpk))); + if ( currency.size() == 0 ) + currency = (char *)"MARMARA"; + if ( firstheight <= lastheight ) firstheight = 0, lastheight = (1 << 30); - if (minamount <= maxamount) + if ( minamount <= maxamount ) minamount = 0, maxamount = (1LL << 60); - result.push_back(Pair("firstheight", static_cast(firstheight))); - result.push_back(Pair("lastheight", static_cast(lastheight))); - result.push_back(Pair("minamount", ValueFromAmount(minamount))); - result.push_back(Pair("maxamount", ValueFromAmount(maxamount))); - result.push_back(Pair("currency", currency)); - if ((n = enum_credit_loops(MARMARA_MARKER_VOUT, totalamount, issuances, totalclosed, closed, cp, firstheight, lastheight, minamount, maxamount, refpk, currency, [](uint256, int32_t) {/*do nothing*/})) > 0) - { - result.push_back(Pair("n", static_cast(n))); - result.push_back(Pair("numpending", static_cast(issuances.size()))); - for (int32_t i = 0; i < issuances.size(); i++) + result.push_back(Pair("firstheight",firstheight)); + result.push_back(Pair("lastheight",lastheight)); + result.push_back(Pair("minamount",ValueFromAmount(minamount))); + result.push_back(Pair("maxamount",ValueFromAmount(maxamount))); + result.push_back(Pair("currency",currency)); + if ( (n= MarmaraGetCreditloops(totalamount,issuances,totalclosed,closed,cp,firstheight,lastheight,minamount,maxamount,refpk,currency)) > 0 ) + { + result.push_back(Pair("n",n)); + matches = (int32_t)issuances.size(); + result.push_back(Pair("pending",matches)); + for (i=0; i(closed.size()))); - for (int32_t i = 0; i < closed.size(); i++) + result.push_back(Pair("issuances",a)); + result.push_back(Pair("totalamount",ValueFromAmount(totalamount))); + matches = (int32_t)closed.size(); + result.push_back(Pair("numclosed",matches)); + for (i=0; i= 64) - { - CCerror = "wallet already has 64 activated split addresses. Use a clean wallet with enough inputs on my-pubkey in it"; - return std::string(); - } - - std::map> segidKeys; - - // add mypubkey - char myactivated1of2addr[KOMODO_ADDRESS_BUFSIZE]; - GetCCaddress1of2(cp, myactivated1of2addr, marmarapk, mypk); - uint32_t segid = komodo_segid32(myactivated1of2addr) & 0x3f; - if (segidKeys.find(segid) == segidKeys.end()) - { - // add myprivkey key - uint8_t mypriv32[32]; - Myprivkey(mypriv32); - CKey mykey; - mykey.Set(&mypriv32[0], &mypriv32[32], true); - segidKeys[segid] = std::make_pair(mykey, mypk); - } - - while (segidKeys.size() < 64) // until we do not generate keys for all 64 segids - { - uint8_t priv32[32]; - // generate random priv key -#ifndef __WIN32 - OS_randombytes(priv32, sizeof(priv32)); -#else - randombytes_buf(priv32, sizeof(priv32)); -#endif - CKey key; - key.Set(&priv32[0], &priv32[32], true); - CPubKey pubkey = key.GetPubKey(); - CKeyID vchAddress = pubkey.GetID(); - - // get 1of2 address segid - char activated1of2addr[KOMODO_ADDRESS_BUFSIZE]; - GetCCaddress1of2(cp, activated1of2addr, marmarapk, pubkey); - uint32_t segid = komodo_segid32(activated1of2addr) & 0x3f; - if (segidKeys.find(segid) == segidKeys.end()) - { - // add segid's keys - segidKeys[segid] = std::make_pair(key, pubkey); - } - } - - //std::cerr << "amount / 64LL / (CAmount)nutxos=" << (amount / 64LL / (CAmount)nutxos) << " 100LL * txfee=" << 100LL * txfee << std::endl; - - if (AddNormalinputs(mtx, mypk, amount + txfee + MARMARA_ACTIVATED_MARKER_AMOUNT * 64 * nutxos, CC_MAXVINS) > 0) - { - // create tx with 64 * nutxo vouts: - for (const auto &keyPair : segidKeys) - { - for (int32_t i = 0; i < nutxos; i++) - { - if (amount / 64LL / (CAmount)nutxos < 100LL * txfee) - { - CCerror = "amount too low"; - return std::string(); - } - // lock the amount on 1of2 address: - CPubKey segidpk = keyPair.second.second; - - // add ccopret - CScript opret = MarmaraCoinbaseOpret(MARMARA_ACTIVATED, height, segidpk); - // add marmara opret segpk to each cc vout - mtx.vout.push_back(MakeMarmaraCC1of2voutOpret(amount / 64 / nutxos, segidpk, opret)); - } - } - mtx.vout.push_back(MakeCC1vout(EVAL_MARMARA, MARMARA_ACTIVATED_MARKER_AMOUNT, marmarapk)); - std::string hextx = FinalizeCCTx(0, cp, mtx, mypk, txfee, CScript()); - if (hextx.empty()) - { - CCerror = "could not finalize tx"; - return std::string(); - } - - // if tx okay save keys: - pwalletMain->MarkDirty(); - std::string strLabel = ""; - for (const auto &keyPair : segidKeys) - { - CKey key = keyPair.second.first; - CPubKey pubkey = keyPair.second.second; - CKeyID vchAddress = pubkey.GetID(); - - pwalletMain->SetAddressBook(vchAddress, strLabel, "receive"); - - // Don't throw error in case a key is already there - if (pwalletMain->HaveKey(vchAddress)) { - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "key already in the wallet" << std::endl); - } - else { - pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1; - if (!pwalletMain->AddKeyPubKey(key, pubkey)) - { - CCerror = "Error adding key to wallet"; - return std::string(); - } - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "key added to wallet addr=" << EncodeDestination(vchAddress) << std::endl); - } - } - - // whenever a key is imported, we need to scan the whole chain - pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value' - return hextx; - - } - else { - CCerror = "not enough normal inputs or too many input utxos"; - return std::string(); - } -} - -// list activated addresses in the wallet -UniValue MarmaraListActivatedAddresses(CWallet *pwalletMain) -{ - UniValue ret(UniValue::VOBJ); - UniValue retarray(UniValue::VARR); - - vACTIVATED_WALLET_DATA activated; - EnumWalletActivatedAddresses(pwalletMain, activated); - for (const auto &a : activated) - { - UniValue elem(UniValue::VOBJ); - std::string sActivated1of2addr = ACTIVATED_WALLET_DATA_ADDR(a); - uint32_t segid = ACTIVATED_WALLET_DATA_SEGID(a); - CAmount amount = ACTIVATED_WALLET_DATA_AMOUNT(a); - - elem.push_back(std::make_pair("activatedaddress", sActivated1of2addr)); - elem.push_back(std::make_pair("segid", (int32_t)segid)); - elem.push_back(std::make_pair("amount", ValueFromAmount(amount))); - retarray.push_back(elem); - } - ret.push_back(Pair("WalletActivatedAddresses", retarray)); - return ret; -} - -// release activated coins from 64 segids to normal address -std::string MarmaraReleaseActivatedCoins(CWallet *pwalletMain, const std::string &destaddr) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - const CAmount txfee = 10000; - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - CPubKey mypk = pubkey2pk(Mypubkey()); - CPubKey marmarapk = GetUnspendable(cp, NULL); - - vACTIVATED_WALLET_DATA activated; - EnumWalletActivatedAddresses(pwalletMain, activated); - if (activated.size() == 0) - { - CCerror = "no activated coins in the wallet (size==0)"; - return std::string(); - } - - int32_t maxvins = 128; - - if (AddNormalinputs(mtx, mypk, txfee, 5) > 0) - { - CAmount total = 0LL; - for (const auto &a : activated) - { - char activated1of2addr[KOMODO_ADDRESS_BUFSIZE]; - CKey key = ACTIVATED_WALLET_DATA_KEY(a); - CPubKey pk = ACTIVATED_WALLET_DATA_PK(a); - - // skip mypubkey - if (pk != mypk) - { - GetCCaddress1of2(cp, activated1of2addr, marmarapk, pk); - - CC *probeCond = MakeCCcond1of2(EVAL_MARMARA, marmarapk, pk); //add probe condition - CCAddVintxCond(cp, probeCond, key.begin()); - cc_free(probeCond); - - std::vector pubkeys; - CAmount amount = AddMarmaraCCInputs(IsMarmaraActivatedVout, mtx, pubkeys, activated1of2addr, 0, maxvins - mtx.vin.size()); // if total == 0 AddMarmaraCCInputs just calcs but does not adds vins - if (amount > 0) - { - amount = AddMarmaraCCInputs(IsMarmaraActivatedVout, mtx, pubkeys, activated1of2addr, amount, maxvins - mtx.vin.size()); - total += amount; - } - } - } - - if (total == 0) - { - CCerror = "no activated coins in the wallet (total==0)"; - return std::string(); - } - CTxDestination dest = DecodeDestination(destaddr.c_str()); - mtx.vout.push_back(CTxOut(total, GetScriptForDestination(dest))); // where to send activated coins from normal - - int32_t height = komodo_nextheight(); - // as opret creation function MarmaraCoinbaseOpret creates opret only for even blocks - adjust this base height to even value - if ((height & 1) != 0) - height++; - CScript opret = MarmaraCoinbaseOpret(MARMARA_RELEASE, height, mypk); // dummy opret - - std::string hextx = FinalizeCCTx(0, cp, mtx, mypk, txfee, opret); - if (hextx.empty()) - { - CCerror = "could not finalize tx"; - return std::string(); - } - else - return hextx; - } - else - { - CCerror = "insufficient normals for tx fee"; - return std::string(); - } -} - - -// unlock activated coins from mypk to normal address -std::string MarmaraUnlockActivatedCoins(CAmount amount) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - const CAmount txfee = 10000; - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_MARMARA); - CPubKey mypk = pubkey2pk(Mypubkey()); - CPubKey marmarapk = GetUnspendable(cp, NULL); - int32_t maxvins = 128; - - if (AddNormalinputs(mtx, mypk, txfee, 5) > 0) - { - char activated1of2addr[KOMODO_ADDRESS_BUFSIZE]; - CMarmaraActivatedOpretChecker activatedChecker; - GetCCaddress1of2(cp, activated1of2addr, marmarapk, mypk); - - CC *probeCond = MakeCCcond1of2(EVAL_MARMARA, marmarapk, mypk); //add probe condition - CCAddVintxCond(cp, probeCond, NULL); - - std::vector pubkeys; - if (amount == 0) - amount = AddMarmaraCCInputs(IsMarmaraActivatedVout, mtx, pubkeys, activated1of2addr, 0, maxvins); // calc available - amount = AddMarmaraCCInputs(IsMarmaraActivatedVout, mtx, pubkeys, activated1of2addr, amount, maxvins); - if (amount == 0) { - CCerror = "no activated inputs added"; - return std::string(); - } - - mtx.vout.push_back(CTxOut(amount, CScript() << Mypubkey() << OP_CHECKSIG)); // where to send activated coins from normal - LOGSTREAMFN("marmara", CCLOG_INFO, stream << "added amount=" << amount << std::endl); - - int32_t height = komodo_nextheight(); - // as opret creation function MarmaraCoinbaseOpret creates opret only for even blocks - adjust this base height to even value - if ((height & 1) != 0) - height++; - CScript opret = MarmaraCoinbaseOpret(MARMARA_RELEASE, height, mypk); // dummy opret with release funcid - - std::string hextx = FinalizeCCTx(0, cp, mtx, mypk, txfee, opret); - cc_free(probeCond); - if (hextx.empty()) - { - CCerror = "could not finalize tx"; - return std::string(); - } - else - return hextx; - } - else - { - CCerror = "insufficient normals for tx fee"; - return std::string(); - } -} - -// collects PoS statistics -#define POSSTAT_STAKETXADDR 0 -#define POSSTAT_STAKETXTYPE 1 -#define POSSTAT_SEGID 2 -#define POSSTAT_COINBASEAMOUNT 3 -#define POSSTAT_TXCOUNT 4 - -UniValue MarmaraPoSStat(int32_t beginHeight, int32_t endHeight) -{ - UniValue result(UniValue::VOBJ); - UniValue array(UniValue::VARR); - UniValue error(UniValue::VOBJ); - - // old stat: - /* tuple params: is boosted, coinbase normal addr, normal total, coinbase cc addr, cc total, txcount, segid */ - // typedef std::tuple TStatElem; - - /* tuple params: is boosted, coinbase normal addr, normal total, coinbase cc addr, cc total, txcount, segid */ - typedef std::tuple TStatElem; - std::map mapStat; - - if (beginHeight == 0) - beginHeight = 1; - if (endHeight == 0) - endHeight = chainActive.Height(); - - for(int32_t h = beginHeight; h <= endHeight; h ++) - { - int8_t hsegid = komodo_segid(0, h); - if (hsegid >= 0) - { - CBlockIndex *pblockindex = chainActive[h]; - CBlock block; - - if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) { - error.push_back(Pair("result", "error")); - error.push_back(Pair("error", std::string("Block not available (pruned data), h=") + std::to_string(h))); - return error; - } - - if (!ReadBlockFromDisk(block, pblockindex, 1)) { - error.push_back(Pair("result", "error")); - error.push_back(Pair("error", std::string("Can't read block from disk, h=") + std::to_string(h))); - return error; - } - - if (block.vtx.size() >= 2) - { - CTransaction coinbase = block.vtx[0]; - CTransaction stakeTx = block.vtx.back(), vintx; - uint256 hashBlock; - vscript_t vopret; - - // check vin.size and vout.size, do not do this yet for diagnosis - // if (stakeTx.vin.size() == 1 && stakeTx.vout.size() == 1 || stakeTx.vout.size() == 2 && GetOpReturnData(stakeTx.vout.back().scriptPubKey, vopret) /*opret with merkle*/) - // { - //if (myGetTransaction(stakeTx.vin[0].prevout.hash, vintx, hashBlock)) - //{ - //char vintxaddr[KOMODO_ADDRESS_BUFSIZE]; - char staketxaddr[KOMODO_ADDRESS_BUFSIZE]; - //Getscriptaddress(vintxaddr, vintx.vout[0].scriptPubKey); - Getscriptaddress(staketxaddr, stakeTx.vout[0].scriptPubKey); - - //if (strcmp(vintxaddr, staketxaddr) == 0) - //{ - - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "h=" << h << " stake txid=" << stakeTx.GetHash().GetHex() << " vout.size()=" << stakeTx.vout.size() << std::endl); - - //char coinbaseaddr[KOMODO_ADDRESS_BUFSIZE]; - //Getscriptaddress(coinbaseaddr, coinbase.vout[0].scriptPubKey); - - std::string sStakeTxAddr = staketxaddr; - std::string staketxtype; - - if (stakeTx.vout[0].scriptPubKey.IsPayToCryptoCondition()) - { - CMarmaraActivatedOpretChecker activatedChecker; - CMarmaraLockInLoopOpretChecker lclChecker(CHECK_ONLY_CCOPRET); - CScript opret; - CPubKey opretpk; - vscript_t vopret; - - if (get_either_opret(&activatedChecker, stakeTx, 0, opret, opretpk) && GetOpReturnData(opret, vopret) && vopret.size() >= 2) - { - if (IsFuncidOneOf(vopret[1], { MARMARA_COINBASE, MARMARA_ACTIVATED })) - { - staketxtype = "activated-1x"; - } - else if (vopret[1] == MARMARA_COINBASE_3X) - { - staketxtype = "activated-3x"; - } - else - { - staketxtype = "activated-unknown"; - } - } - else if (get_either_opret(&lclChecker, stakeTx, 0, opret, opretpk) && GetOpReturnData(opret, vopret) && vopret.size() >= 2) - { - staketxtype = "boosted"; - } - else - { - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "could not get stake tx opret txid=" << stakeTx.GetHash().GetHex() << " h=" << h << std::endl); - error.push_back(Pair("result", "error")); - error.push_back(Pair("error", std::string("Stake transaction opret not recognized, h=") + std::to_string(h))); - return error; - } - } - else - { - staketxtype = "normal"; - } - - TStatElem elem = mapStat[sStakeTxAddr + staketxtype]; - - CAmount amount = std::get(elem) + coinbase.vout[0].nValue; - uint32_t segid = komodo_segid32(staketxaddr) & 0x3f; - mapStat[sStakeTxAddr + staketxtype] = std::make_tuple(sStakeTxAddr, staketxtype, segid, amount, std::get(elem) + 1); - - //} - //} - //} - } - else - LOGSTREAMFN("marmara", CCLOG_ERROR, stream << "not a pos block" << " h=" << h << " hsegid=" << (int)hsegid<< std::endl); - } - } - - for (const auto &eStat : mapStat) - { - UniValue elem(UniValue::VOBJ); - - elem.push_back(Pair("StakeTxAddress", std::get(eStat.second))); - elem.push_back(Pair("StakeTxType", std::get(eStat.second))); - elem.push_back(Pair("segid", (uint64_t)std::get(eStat.second))); - elem.push_back(Pair("CoinbaseAmount", std::get(eStat.second))); - elem.push_back(Pair("StakeTxCount", std::get(eStat.second))); - array.push_back(elem); - } - - result.push_back(Pair("result", "success")); - result.push_back(Pair("BeginHeight", beginHeight)); - result.push_back(Pair("EndHeight", endHeight)); - result.push_back(Pair("StakingStat", array)); - return result; -} diff --git a/src/cc/old/CCassetsCore_v0.cpp b/src/cc/old/CCassetsCore_v0.cpp deleted file mode 100644 index 4e482ce0af0..00000000000 --- a/src/cc/old/CCassetsCore_v0.cpp +++ /dev/null @@ -1,655 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2018 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include "CCtokens_v0.h" -#include "CCassets_v0.h" - -namespace tokensv0 { - -/* - The SetAssetFillamounts() and ValidateAssetRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively. - - This pair of functions are critical to make sure the trading is correct and is the trickiest part of the assets contract. - - //vin.0: normal input - //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0] - //vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue - //vout.0: remaining amount of bid to unspendable - //vout.1: vin.1 value to signer of vin.2 - //vout.2: vin.2 assetoshis to original pubkey - //vout.3: CC output for assetoshis change (if any) - //vout.4: normal output for change (if any) - //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey] - ValidateAssetRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits); - - Yes, this is quite confusing... - - In ValidateAssetRemainder the naming convention is nValue is the coin/asset with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or assets, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits. - - We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it. -*/ - -bool ValidateBidRemainder(int64_t remaining_units,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidunits,int64_t totalunits) -{ - int64_t unitprice,recvunitprice,newunitprice=0; - if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) - { - fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); - return(false); - } - else if ( totalunits != (remaining_units + paidunits) ) - { - fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_units %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_units + paidunits),(long long)remaining_units,(long long)paidunits); - return(false); - } - else if ( orig_nValue != (remaining_nValue + received_nValue) ) - { - fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); - return(false); - } - else - { - //unitprice = (orig_nValue * COIN) / totalunits; - //recvunitprice = (received_nValue * COIN) / paidunits; - //if ( remaining_units != 0 ) - // newunitprice = (remaining_nValue * COIN) / remaining_units; - unitprice = (orig_nValue / totalunits); - recvunitprice = (received_nValue / paidunits); - if ( remaining_units != 0 ) - newunitprice = (remaining_nValue / remaining_units); - if ( recvunitprice < unitprice ) - { - fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); - return(false); - } - fprintf(stderr,"ValidateAssetRemainder() orig %llu total %llu, recv %llu paid %llu,recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(long long)orig_nValue,(long long)totalunits,(long long)received_nValue,(long long)paidunits,(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); - } - return(true); -} - -bool SetBidFillamounts(int64_t &received_nValue,int64_t &remaining_units,int64_t orig_nValue,int64_t &paidunits,int64_t totalunits) -{ - int64_t remaining_nValue,unitprice; double dprice; - if ( totalunits == 0 ) - { - received_nValue = remaining_units = paidunits = 0; - return(false); - } - if ( paidunits >= totalunits ) - { - paidunits = totalunits; - received_nValue = orig_nValue; - remaining_units = 0; - fprintf(stderr,"SetBidFillamounts() bid order totally filled!\n"); - return(true); - } - remaining_units = (totalunits - paidunits); - //unitprice = (orig_nValue * COIN) / totalunits; - //received_nValue = (paidunits * unitprice) / COIN; - unitprice = (orig_nValue / totalunits); - received_nValue = (paidunits * unitprice); - if ( unitprice > 0 && received_nValue > 0 && received_nValue <= orig_nValue ) - { - remaining_nValue = (orig_nValue - received_nValue); - printf("SetBidFillamounts() total.%llu - paid.%llu, remaining %llu <- %llu (%llu - %llu)\n",(long long)totalunits,(long long)paidunits,(long long)remaining_nValue,(long long)(orig_nValue - received_nValue),(long long)orig_nValue,(long long)received_nValue); - return(ValidateBidRemainder(remaining_units,remaining_nValue,orig_nValue,received_nValue,paidunits,totalunits)); - } else return(false); -} - -bool SetAskFillamounts(int64_t &received_assetoshis,int64_t &remaining_nValue,int64_t orig_assetoshis,int64_t &paid_nValue,int64_t total_nValue) -{ - int64_t remaining_assetoshis; double dunitprice; - if ( total_nValue == 0 ) - { - received_assetoshis = remaining_nValue = paid_nValue = 0; - return(false); - } - if ( paid_nValue >= total_nValue ) - { - paid_nValue = total_nValue; - received_assetoshis = orig_assetoshis; - remaining_nValue = 0; - fprintf(stderr,"SetAskFillamounts() ask order totally filled!\n"); - return(true); - } - remaining_nValue = (total_nValue - paid_nValue); - dunitprice = ((double)total_nValue / orig_assetoshis); - received_assetoshis = (paid_nValue / dunitprice); - fprintf(stderr,"SetAskFillamounts() remaining_nValue %.8f (%.8f - %.8f)\n",(double)remaining_nValue/COIN,(double)total_nValue/COIN,(double)paid_nValue/COIN); - fprintf(stderr,"SetAskFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); - if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis ) - { - remaining_assetoshis = (orig_assetoshis - received_assetoshis); - return(ValidateAskRemainder(remaining_nValue,remaining_assetoshis,orig_assetoshis,received_assetoshis,paid_nValue,total_nValue)); - } else return(false); -} - -bool ValidateAskRemainder(int64_t remaining_nValue,int64_t remaining_assetoshis,int64_t orig_assetoshis,int64_t received_assetoshis,int64_t paid_nValue,int64_t total_nValue) -{ - int64_t unitprice,recvunitprice,newunitprice=0; - if ( orig_assetoshis == 0 || received_assetoshis == 0 || paid_nValue == 0 || total_nValue == 0 ) - { - fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis == %llu || received_assetoshis == %llu || paid_nValue == %llu || total_nValue == %llu\n",(long long)orig_assetoshis,(long long)received_assetoshis,(long long)paid_nValue,(long long)total_nValue); - return(false); - } - else if ( total_nValue != (remaining_nValue + paid_nValue) ) - { - fprintf(stderr,"ValidateAssetRemainder() total_nValue %llu != %llu (remaining_nValue %llu + %llu paid_nValue)\n",(long long)total_nValue,(long long)(remaining_nValue + paid_nValue),(long long)remaining_nValue,(long long)paid_nValue); - return(false); - } - else if ( orig_assetoshis != (remaining_assetoshis + received_assetoshis) ) - { - fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_assetoshis,(long long)(remaining_assetoshis - received_assetoshis),(long long)remaining_assetoshis,(long long)received_assetoshis); - return(false); - } - else - { - unitprice = (total_nValue / orig_assetoshis); - recvunitprice = (paid_nValue / received_assetoshis); - if ( remaining_nValue != 0 ) - newunitprice = (remaining_nValue / remaining_assetoshis); - if ( recvunitprice < unitprice ) - { - fprintf(stderr,"ValidateAskRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); - return(false); - } - fprintf(stderr,"ValidateAskRemainder() got recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); - } - return(true); -} - -bool SetSwapFillamounts(int64_t &received_assetoshis,int64_t &remaining_assetoshis2,int64_t orig_assetoshis,int64_t &paid_assetoshis2,int64_t total_assetoshis2) -{ - int64_t remaining_assetoshis; double dunitprice; - if ( total_assetoshis2 == 0 ) - { - fprintf(stderr,"SetSwapFillamounts() total_assetoshis2.0 origsatoshis.%llu paid_assetoshis2.%llu\n",(long long)orig_assetoshis,(long long)paid_assetoshis2); - received_assetoshis = remaining_assetoshis2 = paid_assetoshis2 = 0; - return(false); - } - if ( paid_assetoshis2 >= total_assetoshis2 ) - { - paid_assetoshis2 = total_assetoshis2; - received_assetoshis = orig_assetoshis; - remaining_assetoshis2 = 0; - fprintf(stderr,"SetSwapFillamounts() swap order totally filled!\n"); - return(true); - } - remaining_assetoshis2 = (total_assetoshis2 - paid_assetoshis2); - dunitprice = ((double)total_assetoshis2 / orig_assetoshis); - received_assetoshis = (paid_assetoshis2 / dunitprice); - fprintf(stderr,"SetSwapFillamounts() remaining_assetoshis2 %llu (%llu - %llu)\n",(long long)remaining_assetoshis2/COIN,(long long)total_assetoshis2/COIN,(long long)paid_assetoshis2/COIN); - fprintf(stderr,"SetSwapFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); - if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis ) - { - remaining_assetoshis = (orig_assetoshis - received_assetoshis); - return(ValidateAskRemainder(remaining_assetoshis2,remaining_assetoshis,orig_assetoshis,received_assetoshis,paid_assetoshis2,total_assetoshis2)); - } else return(false); -} - -bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidunits,int64_t totalunits) -{ - int64_t unitprice,recvunitprice,newunitprice=0; - if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) - { - fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); - return(false); - } - else if ( totalunits != (remaining_price + paidunits) ) - { - fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_price %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_price + paidunits),(long long)remaining_price,(long long)paidunits); - return(false); - } - else if ( orig_nValue != (remaining_nValue + received_nValue) ) - { - fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); - return(false); - } - else - { - unitprice = (orig_nValue * COIN) / totalunits; - recvunitprice = (received_nValue * COIN) / paidunits; - if ( remaining_price != 0 ) - newunitprice = (remaining_nValue * COIN) / remaining_price; - if ( recvunitprice < unitprice ) - { - fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); - return(false); - } - fprintf(stderr,"ValidateAssetRemainder() recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); - } - return(true); -} - -/* use EncodeTokenCreateOpRet instead: -CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector origpubkey,std::string name,std::string description) -{ - CScript opret; uint8_t evalcode = EVAL_ASSETS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description); - return(opret); -} -*/ - -vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector origpubkey) -{ - vscript_t vopret; - uint8_t evalcode = EVAL_ASSETS; - - switch ( assetFuncId ) - { - //case 't': this cannot be here - case 'x': case 'o': - vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId); - break; - case 's': case 'b': case 'S': case 'B': - vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId << price << origpubkey); - break; - case 'E': case 'e': - assetid2 = revuint256(assetid2); - vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId << assetid2 << price << origpubkey); - break; - default: - fprintf(stderr,"EncodeAssetOpRet: illegal funcid.%02x\n", assetFuncId); - //opret << OP_RETURN; - break; - } - return(vopret); -} - -/* it is for compatibility, do not use this for new contracts (use DecodeTokenCreateOpRet) -bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description) -{ - std::vector vopret; uint8_t evalcode,funcid,*script; - GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if ( script != 0 && vopret.size() > 2 && script[0] == EVAL_ASSETS && script[1] == 'c' ) - { - if ( E_UNMARSHAL(vopret,ss >> evalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description) != 0 ) - return(true); - } - return(0); -} */ - -uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey) -{ - vscript_t vopretAssets; //, vopretAssetsStripped; - uint8_t *script, funcId = 0, assetsFuncId = 0, dummyEvalCode, dummyAssetFuncId; - uint256 dummyTokenid; - std::vector voutPubkeysDummy; - std::vector> oprets; - - tokenid = zeroid; - assetid2 = zeroid; - price = 0; - assetsEvalCode = 0; - assetsFuncId = 0; - - // First - decode token opret: - funcId = DecodeTokenOpRet(scriptPubKey, dummyEvalCode, tokenid, voutPubkeysDummy, oprets); - GetOpretBlob(oprets, OPRETID_ASSETSDATA, vopretAssets); - - LOGSTREAM((char *)"ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() from DecodeTokenOpRet returned funcId=" << (int)funcId << std::endl); - - if (funcId == 0 || vopretAssets.size() < 2) { - LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << std::endl); - return (uint8_t)0; - } - - //if (!E_UNMARSHAL(vopretAssets, { ss >> vopretAssetsStripped; })) { //strip string size - // std::cerr << "DecodeAssetTokenOpRet() could not unmarshal vopretAssetsStripped" << std::endl; - // return (uint8_t)0; - //} - - // additional check to prevent crash - if (vopretAssets.size() >= 2) { - - assetsEvalCode = vopretAssets.begin()[0]; - assetsFuncId = vopretAssets.begin()[1]; - - LOGSTREAM((char *)"ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() assetsEvalCode=" << (int)assetsEvalCode << " funcId=" << (char)(funcId ? funcId : ' ') << " assetsFuncId=" << (char)(assetsFuncId ? assetsFuncId : ' ') << std::endl); - - if (assetsEvalCode == EVAL_ASSETS) - { - //fprintf(stderr,"DecodeAssetTokenOpRet() decode.[%c] assetFuncId.[%c]\n", funcId, assetFuncId); - switch (assetsFuncId) - { - case 'x': case 'o': - if (vopretAssets.size() == 2) // no data after 'evalcode assetFuncId' allowed - { - return(assetsFuncId); - } - break; - case 's': case 'b': case 'S': case 'B': - if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0) - { - //fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price); - return(assetsFuncId); - } - break; - case 'E': case 'e': - if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0) - { - //fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price); - assetid2 = revuint256(assetid2); - return(assetsFuncId); - } - break; - default: - break; - } - } - } - - LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() no asset's payload or incorrect assets funcId or evalcode" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << " assetsEvalCode=" << assetsEvalCode << " assetsFuncId=" << assetsFuncId << std::endl); - return (uint8_t)0; -} - -// extract sell/buy owner's pubkey from the opret -bool SetAssetOrigpubkey(std::vector &origpubkey,int64_t &price,const CTransaction &tx) -{ - uint256 assetid,assetid2; - uint8_t evalCode; - - if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey) != 0 ) - return(true); - else - return(false); -} - -// Calculate seller/buyer's dest cc address from ask/bid tx funcid -bool GetAssetorigaddrs(struct CCcontract_info *cp, char *origCCaddr, char *origNormalAddr, const CTransaction& vintx) -{ - uint256 assetid, assetid2; - int64_t price,nValue=0; - int32_t n; - uint8_t vintxFuncId; - std::vector origpubkey; - CScript script; - uint8_t evalCode; - - n = vintx.vout.size(); - if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 ) - return(false); - - bool bGetCCaddr = false; - struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - - if (vintxFuncId == 's' || vintxFuncId == 'S') { - // bGetCCaddr = GetCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); - cpTokens->evalcodeNFT = cp->evalcodeNFT; // add non-fungible if present - bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible - } - else if (vintxFuncId == 'b' || vintxFuncId == 'B') { - cpTokens->evalcodeNFT = cp->evalcodeNFT; // add non-fungible if present - bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible - } - else { - std::cerr << "GetAssetorigaddrs incorrect vintx funcid=" << (char)(vintxFuncId?vintxFuncId:' ') << std::endl; - return false; - } - if( bGetCCaddr && Getscriptaddress(origNormalAddr, CScript() << origpubkey << OP_CHECKSIG)) - return(true); - else - return(false); -} - - -int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *origCCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx) -{ - uint256 hashBlock; - uint256 assetid, assetid2; - int64_t tmpprice; - std::vector tmporigpubkey; - uint8_t evalCode; - - char destaddr[64], unspendableAddr[64]; - - origaddr[0] = destaddr[0] = origCCaddr[0] = 0; - - uint8_t funcid = 0; - if (tx.vout.size() > 0) { - uint256 assetid, assetid2; - int64_t tmpprice; - std::vector tmporigpubkey; - uint8_t evalCode; - - funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey); - } - - if( tx.vin.size() < 2 ) - return eval->Invalid("not enough for CC vins"); - else if( tx.vin[vini].prevout.n != 0 ) - return eval->Invalid("vin1 needs to be buyvin.vout[0]"); - else if( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash, vinTx,hashBlock) == 0 ) - { - std::cerr << "AssetValidateCCvin() cannot load vintx for vin=" << vini << " vintx id=" << tx.vin[vini].prevout.hash.GetHex() << std::endl; - return eval->Invalid("always should find CCvin, but didnt"); - } - // check source cc unspendable cc address: - // if fillSell or cancelSell --> should spend tokens from dual-eval token-assets unspendable addr - else if( (funcid == 'S' || funcid == 'x') && - (Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || - !GetTokensCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) || - strcmp(destaddr, unspendableAddr) != 0)) - { - fprintf(stderr,"AssetValidateCCvin() cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); - return eval->Invalid("invalid vin assets CCaddr"); - } - // if fillBuy or cancelBuy --> should spend coins from asset unspendable addr - else if ((funcid == 'B' || funcid == 'o') && - (Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || - !GetCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) || - strcmp(destaddr, unspendableAddr) != 0)) - { - fprintf(stderr, "AssetValidateCCvin() cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); - return eval->Invalid("invalid vin assets CCaddr"); - } - // end of check source unspendable cc address - //else if ( vinTx.vout[0].nValue < 10000 ) - // return eval->Invalid("invalid dust for buyvin"); - // get user dest cc and normal addresses: - else if( GetAssetorigaddrs(cp, origCCaddr, origaddr, vinTx) == 0 ) - return eval->Invalid("couldnt get origaddr for buyvin"); - - //fprintf(stderr,"AssetValidateCCvin() got %.8f to origaddr.(%s)\n", (double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr); - - if ( vinTx.vout[0].nValue == 0 ) - return eval->Invalid("null value CCvin"); - - return(vinTx.vout[0].nValue); -} - -int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid) -{ - CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid, evalCode; - - CCaddr[0] = origaddr[0] = 0; - - // validate locked coins on Assets vin[1] - if ( (nValue= AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 ) - return(0); - else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("invalid normal vout0 for buyvin"); - else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey)) == 'b' && - vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) // marker is only in 'b'? - return eval->Invalid("invalid normal vout1 for buyvin"); - else - { - //fprintf(stderr,"have %.8f checking assetid origaddr.(%s)\n",(double)nValue/COIN,origaddr); - if ( vinTx.vout.size() > 0 && funcid != 'b' && funcid != 'B' ) - return eval->Invalid("invalid opreturn for buyvin"); - else if ( refassetid != assetid ) - return eval->Invalid("invalid assetid for buyvin"); - //int32_t i; for (i=31; i>=0; i--) - // fprintf(stderr,"%02x",((uint8_t *)&assetid)[i]); - //fprintf(stderr," AssetValidateBuyvin assetid for %s\n",origaddr); - } - return(nValue); -} - -int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid) -{ - CTransaction vinTx; int64_t nValue,assetoshis; - //fprintf(stderr,"AssetValidateSellvin()\n"); - if ( (nValue = AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 ) - return(0); - if ( (assetoshis= IsAssetvout(cp, tmpprice, tmporigpubkey, vinTx, 0, assetid)) == 0 ) - return eval->Invalid("invalid missing CC vout0 for sellvin"); - else - return(assetoshis); -} - - -// validates opret for asset tx: -bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &price, std::vector &origpubkey) { - - uint256 assetidOpret, assetidOpret2; - uint8_t funcid, evalCode; - - // this is just for log messages indentation fur debugging recursive calls: - int32_t n = tx.vout.size(); - - if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey)) == 0) - { - std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned funcId=0 for opret from txid=" << tx.GetHash().GetHex() << std::endl; - return(false); - } -/* it is now on token level: - else if (funcid == 'c') - { - if (assetid != zeroid && assetid == tx.GetHash() && v == 0) { - //std::cerr << "ValidateAssetOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl; - return(true); - } - } - else if (funcid == 't') // TODO: check if this new block does not influence IsAssetVout - { - //std::cerr << "ValidateAssetOpret() assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; - if (assetid != zeroid && assetid == assetidOpret) { - //std::cerr << "ValidateAssetOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl; - return(true); - } - } */ - //else if ((funcid == 'b' || funcid == 'B') && v == 0) // critical! 'b'/'B' vout0 is NOT asset - // return(false); - else if (funcid != 'E') - { - if (assetid != zeroid && assetidOpret == assetid) - { - //std::cerr << "ValidateAssetOpret() returns true for not 'E', funcid=" << (char)funcid << std::endl; - return(true); - } - } - else if (funcid == 'E') // NOTE: not implemented yet! - { - if (v < 2 && assetid != zeroid && assetidOpret == assetid) - return(true); - else if (v == 2 && assetid != zeroid && assetidOpret2 == assetid) - return(true); - } - - //std::cerr << "ValidateAssetOpret() return false funcid=" << (char)funcid << " assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; - return false; -} - -// Checks if the vout is a really Asset CC vout -int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid) -{ - - //std::cerr << "IsAssetvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for assetid=" << refassetid.GetHex() << std::endl; - - int32_t n = tx.vout.size(); - // just check boundaries: - if (v >= n - 1) { // just moved this up (dimxy) - std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl; - return(0); - } - - if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here - { - // moved opret checking to this new reusable func (dimxy): - const bool valOpret = ValidateAssetOpret(tx, v, refassetid, price, origpubkey); - //std::cerr << "IsAssetvout() ValidateAssetOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl; - if (valOpret) { - //std::cerr << "IsAssetvout() ValidateAssetOpret returned true, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl; - return tx.vout[v].nValue; - } - - //fprintf(stderr,"IsAssetvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str()); - } - //fprintf(stderr,"IsAssetvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN); - return(0); -} - -// sets cc inputs vs cc outputs and ensures they are equal: -bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid) -{ - CTransaction vinTx; uint256 hashBlock, id, id2; int32_t flag; int64_t assetoshis; std::vector tmporigpubkey; int64_t tmpprice; - int32_t numvins = tx.vin.size(); - int32_t numvouts = tx.vout.size(); - inputs = outputs = 0; - - struct CCcontract_info *cpTokens, C; - - cpTokens = CCinit(&C, EVAL_TOKENS); - - for (int32_t i = 0; iismyvin)(tx.vin[i].scriptSig)*/ (*cpTokens->ismyvin)(tx.vin[i].scriptSig) ) // || IsVinAllowed(tx.vin[i].scriptSig) != 0) - { - //std::cerr << indentStr << "AssetExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl; - // we are not inside the validation code -- dimxy - if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock))) - { - std::cerr << "AssetCalcAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl; - return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt"); - } - else { - // validate vouts of vintx - //std::cerr << indentStr << "AssetExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl; - //assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, vinTx, tx.vin[i].prevout.n, assetid); - std::vector vopretExtra; - std::vector vinPubkeysEmpty; - - // TODO: maybe we do not need call to IsTokensVout here, cause we've already selected token vins - assetoshis = IsTokensvout(false, false, cpTokens, NULL, vinTx, tx.vin[i].prevout.n, assetid); - if (assetoshis != 0) - { - //std::cerr << "AssetCalcAmounts() vin i=" << i << " assetoshis=" << assetoshis << std::endl; - inputs += assetoshis; - } - } - } - } - - for (int32_t i = 0; i < numvouts-1; i++) - { - assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, tx, i, assetid); - if (assetoshis != 0) - { - //std::cerr << "AssetCalcAmounts() vout i=" << i << " assetoshis=" << assetoshis << std::endl; - outputs += assetoshis; - } - } - - //std::cerr << "AssetCalcAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; - - /* we do not verify inputs == outputs here, - it's now done in Tokens */ - return(true); -} - -}; \ No newline at end of file diff --git a/src/cc/old/CCassets_v0.h b/src/cc/old/CCassets_v0.h deleted file mode 100644 index e0163dccc37..00000000000 --- a/src/cc/old/CCassets_v0.h +++ /dev/null @@ -1,69 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - - -/* - CCassetstx has the functions that create the EVAL_ASSETS transactions. It is expected that rpc calls would call these functions. For EVAL_ASSETS, the rpc functions are in rpcwallet.cpp - - CCassetsCore has functions that are used in two contexts, both during rpc transaction create time and also during the blockchain validation. Using the identical functions is a good way to prevent them from being mismatched. The must match or the transaction will get rejected. - */ - -#ifndef CC_ASSETS_V0_H -#define CC_ASSETS_V0_H - -#include "../CCinclude.h" - -namespace tokensv0 { - -// CCcustom -bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); - -// CCassetsCore -vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector origpubkey); -uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey); -bool SetAssetOrigpubkey(std::vector &origpubkey,int64_t &price,const CTransaction &tx); -int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid); -bool ValidateBidRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice); -bool ValidateAskRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice); -bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice); -bool SetBidFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice); -bool SetAskFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice); -bool SetSwapFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice); -int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid); -int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid); -bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid); - -// CCassetstx -//int64_t GetAssetBalance(CPubKey pk,uint256 tokenid); // --> GetTokenBalance() -int64_t AddAssetInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 assetid, int64_t total, int32_t maxinputs); - -UniValue AssetOrders(uint256 tokenid, CPubKey pubkey, uint8_t additionalEvalCode); -//UniValue AssetInfo(uint256 tokenid); -//UniValue AssetList(); -//std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description); -//std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector destpubkey,int64_t total); -//std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector destpubkey,int64_t total,int32_t evalcode); - -std::string CreateBuyOffer(int64_t txfee,int64_t bidamount,uint256 assetid,int64_t pricetotal); -std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid); -std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t fillamount); -std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t pricetotal); -std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 assetid2,int64_t pricetotal); -std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid); -std::string FillSell(int64_t txfee,uint256 assetid,uint256 assetid2,uint256 asktxid,int64_t fillamount); - -}; - -#endif diff --git a/src/cc/old/CCassetstx_v0.cpp b/src/cc/old/CCassetstx_v0.cpp deleted file mode 100644 index b003c40d30e..00000000000 --- a/src/cc/old/CCassetstx_v0.cpp +++ /dev/null @@ -1,784 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include "CCassets_v0.h" -#include "CCtokens_v0.h" - -namespace tokensv0 { - - -UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t additionalEvalCode) -{ - UniValue result(UniValue::VARR); - - struct CCcontract_info *cpAssets, assetsC; - struct CCcontract_info *cpTokens, tokensC; - - cpAssets = CCinit(&assetsC, EVAL_ASSETS); - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - - auto addOrders = [&](struct CCcontract_info *cp, std::vector >::const_iterator it) - { - uint256 txid, hashBlock, assetid, assetid2; - int64_t price; - std::vector origpubkey; - CTransaction ordertx; - uint8_t funcid, evalCode; - char numstr[32], funcidstr[16], origaddr[64], origtokenaddr[64]; - - txid = it->first.txhash; - LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() checking txid=" << txid.GetHex() << std::endl); - if ( myGetTransaction(txid, ordertx, hashBlock) != 0 ) - { - // for logging: funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey); - if (ordertx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(ordertx.vout[ordertx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0) - { - LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() checking ordertx.vout.size()=" << ordertx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << " assetid=" << assetid.GetHex() << std::endl); - - if (pk == CPubKey() && (refassetid == zeroid || assetid == refassetid) // tokenorders - || pk != CPubKey() && pk == pubkey2pk(origpubkey) && (funcid == 'S' || funcid == 's')) // mytokenorders, returns only asks (is this correct?) - { - - LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() it->first.index=" << it->first.index << " ordertx.vout[it->first.index].nValue=" << ordertx.vout[it->first.index].nValue << std::endl); - if (ordertx.vout[it->first.index].nValue == 0) { - LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() order with value=0 skipped" << std::endl); - return; - } - - UniValue item(UniValue::VOBJ); - - funcidstr[0] = funcid; - funcidstr[1] = 0; - item.push_back(Pair("funcid", funcidstr)); - item.push_back(Pair("txid", txid.GetHex())); - item.push_back(Pair("vout", (int64_t)it->first.index)); - if (funcid == 'b' || funcid == 'B') - { - sprintf(numstr, "%.8f", (double)ordertx.vout[it->first.index].nValue / COIN); - item.push_back(Pair("amount", numstr)); - sprintf(numstr, "%.8f", (double)ordertx.vout[0].nValue / COIN); - item.push_back(Pair("bidamount", numstr)); - } - else - { - sprintf(numstr, "%llu", (long long)ordertx.vout[it->first.index].nValue); - item.push_back(Pair("amount", numstr)); - sprintf(numstr, "%llu", (long long)ordertx.vout[0].nValue); - item.push_back(Pair("askamount", numstr)); - } - if (origpubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) - { - GetCCaddress(cp, origaddr, pubkey2pk(origpubkey)); - item.push_back(Pair("origaddress", origaddr)); - GetTokensCCaddress(cpTokens, origtokenaddr, pubkey2pk(origpubkey)); - item.push_back(Pair("origtokenaddress", origtokenaddr)); - } - if (assetid != zeroid) - item.push_back(Pair("tokenid", assetid.GetHex())); - if (assetid2 != zeroid) - item.push_back(Pair("otherid", assetid2.GetHex())); - if (price > 0) - { - if (funcid == 's' || funcid == 'S' || funcid == 'e' || funcid == 'e') - { - sprintf(numstr, "%.8f", (double)price / COIN); - item.push_back(Pair("totalrequired", numstr)); - sprintf(numstr, "%.8f", (double)price / (COIN * ordertx.vout[0].nValue)); - item.push_back(Pair("price", numstr)); - } - else - { - item.push_back(Pair("totalrequired", (int64_t)price)); - sprintf(numstr, "%.8f", (double)ordertx.vout[0].nValue / (price * COIN)); - item.push_back(Pair("price", numstr)); - } - } - result.push_back(item); - LOGSTREAM("ccassets", CCLOG_DEBUG1, stream << "addOrders() added order funcId=" << (char)(funcid ? funcid : ' ') << " it->first.index=" << it->first.index << " ordertx.vout[it->first.index].nValue=" << ordertx.vout[it->first.index].nValue << " tokenid=" << assetid.GetHex() << std::endl); - } - } - } - }; - - std::vector > unspentOutputsTokens, unspentOutputsDualEvalTokens, unspentOutputsCoins; - - char assetsUnspendableAddr[64]; - GetCCaddress(cpAssets, assetsUnspendableAddr, GetUnspendable(cpAssets, NULL)); - SetCCunspents(unspentOutputsCoins, assetsUnspendableAddr,true); - - char assetsTokensUnspendableAddr[64]; - std::vector vopretNonfungible; - if (refassetid != zeroid) { - GetNonfungibleData(refassetid, vopretNonfungible); - if (vopretNonfungible.size() > 0) - cpAssets->evalcodeNFT = vopretNonfungible.begin()[0]; - } - GetTokensCCaddress(cpAssets, assetsTokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); - SetCCunspents(unspentOutputsTokens, assetsTokensUnspendableAddr,true); - - // tokenbids: - for (std::vector >::const_iterator itCoins = unspentOutputsCoins.begin(); - itCoins != unspentOutputsCoins.end(); - itCoins++) - addOrders(cpAssets, itCoins); - - // tokenasks: - for (std::vector >::const_iterator itTokens = unspentOutputsTokens.begin(); - itTokens != unspentOutputsTokens.end(); - itTokens++) - addOrders(cpAssets, itTokens); - - if (additionalEvalCode != 0) { //this would be mytokenorders - char assetsDualEvalTokensUnspendableAddr[64]; - - // try also dual eval tokenasks (and we do not need bids): - cpAssets->evalcodeNFT = additionalEvalCode; - GetTokensCCaddress(cpAssets, assetsDualEvalTokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); - SetCCunspents(unspentOutputsDualEvalTokens, assetsDualEvalTokensUnspendableAddr,true); - - for (std::vector >::const_iterator itDualEvalTokens = unspentOutputsDualEvalTokens.begin(); - itDualEvalTokens != unspentOutputsDualEvalTokens.end(); - itDualEvalTokens++) - addOrders(cpAssets, itDualEvalTokens); - } - return(result); -} - -// not used (use TokenCreate instead) -/* std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk; struct CCcontract_info *cp,C; - if ( assetsupply < 0 ) - { - fprintf(stderr,"negative assetsupply %lld\n",(long long)assetsupply); - return(""); - } - cp = CCinit(&C,EVAL_ASSETS); - if ( name.size() > 32 || description.size() > 4096 ) - { - fprintf(stderr,"name.%d or description.%d is too big\n",(int32_t)name.size(),(int32_t)description.size()); - return(""); - } - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - if ( AddNormalinputs(mtx,mypk,assetsupply+2*txfee,64) > 0 ) - { - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,assetsupply,mypk)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetCreateOpRet('c',Mypubkey(),name,description))); - } - return(""); -} */ - -// not used (use TokenTransfer instead) -/* std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector destpubkey,int64_t total) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk; uint64_t mask; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C; - if ( total < 0 ) - { - fprintf(stderr,"negative total %lld\n",(long long)total); - return(""); - } - cp = CCinit(&C,EVAL_ASSETS); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) - { - //n = outputs.size(); - //if ( n == amounts.size() ) - //{ - // for (i=0; i 0 ) - { - - if (inputs < total) { //added dimxy - std::cerr << "AssetTransfer(): insufficient funds" << std::endl; - return (""); - } - if ( inputs > total ) - CCchange = (inputs - total); - //for (i=0; i destpubkey,int64_t total,int32_t evalcode) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C; - if ( total < 0 ) - { - fprintf(stderr,"negative total %lld\n",(long long)total); - return(""); - } - cp = CCinit(&C,EVAL_ASSETS); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) - { - if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,total,60)) > 0 ) - { - if ( inputs > total ) - CCchange = (inputs - total); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk)); - mtx.vout.push_back(MakeCC1vout(evalcode,total,pubkey2pk(destpubkey))); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey()))); - } else fprintf(stderr,"not enough CC asset inputs for %.8f\n",(double)total/COIN); - } - return(""); -} */ - -// rpc tokenbid implementation, locks 'bidamount' coins for the 'pricetotal' of tokens -std::string CreateBuyOffer(int64_t txfee, int64_t bidamount, uint256 assetid, int64_t pricetotal) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk; - struct CCcontract_info *cpAssets, C; - uint256 hashBlock; - CTransaction vintx; - std::vector origpubkey; - std::string name,description; - int64_t inputs; - - std::cerr << "CreateBuyOffer() bidamount=" << bidamount << " numtokens(pricetotal)=" << pricetotal << std::endl; - - if (bidamount < 0 || pricetotal < 0) - { - fprintf(stderr,"negative bidamount %lld, pricetotal %lld\n", (long long)bidamount, (long long)pricetotal); - return(""); - } - if (myGetTransaction(assetid, vintx, hashBlock) == 0) - { - fprintf(stderr,"cant find assetid\n"); - return(""); - } - if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, origpubkey, name, description) == 0) - { - fprintf(stderr,"assetid isnt assetcreation txid\n"); - return(""); - } - - cpAssets = CCinit(&C,EVAL_ASSETS); // NOTE: assets here! - if (txfee == 0) - txfee = 10000; - - mypk = pubkey2pk(Mypubkey()); - - if ((inputs = AddNormalinputs(mtx, mypk, bidamount+(2*txfee), 64)) > 0) - { - std::cerr << "CreateBuyOffer() inputs=" << inputs << std::endl; - if (inputs < bidamount+txfee) { - std::cerr << "CreateBuyOffer(): insufficient coins to make buy offer" << std::endl; - CCerror = strprintf("insufficient coins to make buy offer"); - return (""); - } - - CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, 0); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendableAssetsPubkey)); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); - std::vector voutTokenPubkeys; // should be empty - no token vouts - - return FinalizeCCTx(0, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, // TODO: actually this tx is not 'tokens', maybe it is better not to have token opret here but only asset opret. - std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('b', zeroid, pricetotal, Mypubkey())))); // But still such token opret should not make problems because no token eval in these vouts - } - CCerror = strprintf("no coins found to make buy offer"); - return(""); -} - -// rpc tokenask implementation, locks 'askamount' tokens for the 'pricetotal' -std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t pricetotal) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk; - uint64_t mask; - int64_t inputs, CCchange; - struct CCcontract_info *cpAssets, assetsC; - struct CCcontract_info *cpTokens, tokensC; - - //std::cerr << "CreateSell() askamount=" << askamount << " pricetotal=" << pricetotal << std::endl; - - if (askamount < 0 || pricetotal < 0) { - fprintf(stderr,"negative askamount %lld, askamount %lld\n",(long long)pricetotal,(long long)askamount); - return(""); - } - - cpAssets = CCinit(&assetsC, EVAL_ASSETS); // NOTE: for signing - - - if (txfee == 0) - txfee = 10000; - - mypk = pubkey2pk(Mypubkey()); - if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) - { - std::vector vopretNonfungible; - mask = ~((1LL << mtx.vin.size()) - 1); - // add single-eval tokens (or non-fungible tokens): - cpTokens = CCinit(&tokensC, EVAL_TOKENS); // NOTE: adding inputs only from EVAL_TOKENS cc - if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, askamount, 60, vopretNonfungible)) > 0) - { - if (inputs < askamount) { - //was: askamount = inputs; - std::cerr << "CreateSell(): insufficient tokens for ask" << std::endl; - CCerror = strprintf("insufficient tokens for ask"); - return (""); - } - - // if this is non-fungible tokens: - if( !vopretNonfungible.empty() ) - // set its evalcode - cpAssets->evalcodeNFT = vopretNonfungible.begin()[0]; - - CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, NULL); - mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, cpAssets->evalcodeNFT, askamount, unspendableAssetsPubkey)); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); //marker (seems, it is not for tokenorders) - if (inputs > askamount) - CCchange = (inputs - askamount); - if (CCchange != 0) - // change to single-eval or non-fungible token vout (although for non-fungible token change currently is not possible) - mtx.vout.push_back(MakeTokensCC1vout((cpAssets->evalcodeNFT) ? cpAssets->evalcodeNFT : EVAL_TOKENS, CCchange, mypk)); - - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(unspendableAssetsPubkey); - - return FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, - std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey())))); - } - else { - fprintf(stderr, "need some tokens to place ask\n"); - } - } - else { // dimxy added 'else', because it was misleading message before - fprintf(stderr, "need some native coins to place ask\n"); - } - return(""); -} - -////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// -std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 assetid2,int64_t pricetotal) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk; uint64_t mask; int64_t inputs,CCchange; CScript opret; struct CCcontract_info *cp,C; - - ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// - fprintf(stderr,"asset swaps disabled\n"); - return(""); - ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// - - if ( askamount < 0 || pricetotal < 0 ) - { - fprintf(stderr,"negative askamount %lld, askamount %lld\n",(long long)pricetotal,(long long)askamount); - return(""); - } - cp = CCinit(&C, EVAL_ASSETS); - - if ( txfee == 0 ) - txfee = 10000; - ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// - mypk = pubkey2pk(Mypubkey()); - - if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) - { - mask = ~((1LL << mtx.vin.size()) - 1); - /*if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0) - { - ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// - if (inputs < askamount) { - //was: askamount = inputs; - std::cerr << "CreateSwap(): insufficient tokens for ask" << std::endl; - CCerror = strprintf("insufficient tokens for ask"); - return (""); - } - ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// - CPubKey unspendablePubkey = GetUnspendable(cp, 0); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, askamount, unspendablePubkey)); - - if (inputs > askamount) - CCchange = (inputs - askamount); - if (CCchange != 0) - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); - - ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// - std::vector voutTokenPubkeys; // should be empty - no token vouts - - if (assetid2 == zeroid) { - opret = EncodeTokenOpRet(assetid, voutTokenPubkeys, - EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey())); - } - else { - opret = EncodeTokenOpRet(assetid, voutTokenPubkeys, - EncodeAssetOpRet('e', assetid2, pricetotal, Mypubkey())); - } - ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// - return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret)); - } - else { - fprintf(stderr, "need some assets to place ask\n"); - } */ - } - else { // dimxy added 'else', because it was misleading message before - fprintf(stderr,"need some native coins to place ask\n"); - } - - return(""); -} ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// - -// unlocks coins -std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction vintx; uint64_t mask; - uint256 hashBlock; int64_t bidamount; - CPubKey mypk; struct CCcontract_info *cpAssets, C; - uint8_t funcid,dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector dummyOrigpubkey; - - cpAssets = CCinit(&C, EVAL_ASSETS); - - if (txfee == 0) - txfee = 10000; - - mypk = pubkey2pk(Mypubkey()); - - if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) - { - mask = ~((1LL << mtx.vin.size()) - 1); - if (myGetTransaction(bidtxid, vintx, hashBlock) != 0) - { - std::vector vopretNonfungible; - GetNonfungibleData(assetid, vopretNonfungible); - - bidamount = vintx.vout[0].nValue; - mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); // coins in Assets - - if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) - { - if (funcid == 's') mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b' - else if (funcid=='S') mtx.vin.push_back(CTxIn(bidtxid, 3, CScript())); // spend marker if funcid='B' - } - - mtx.vout.push_back(CTxOut(bidamount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - - std::vector voutTokenPubkeys; // should be empty, no token vouts - - return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, - std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('o', zeroid, 0, Mypubkey()))))); - } - } - return(""); -} - -//unlocks tokens -std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction vintx; uint64_t mask; - uint256 hashBlock; int64_t askamount; - CPubKey mypk; - struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC; - uint8_t funcid, dummyEvalCode; - uint256 dummyAssetid, dummyAssetid2; - int64_t dummyPrice; - std::vector dummyOrigpubkey; - - cpAssets = CCinit(&assetsC, EVAL_ASSETS); - - if (txfee == 0) - txfee = 10000; - - mypk = pubkey2pk(Mypubkey()); - - if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) - { - mask = ~((1LL << mtx.vin.size()) - 1); - if (myGetTransaction(asktxid, vintx, hashBlock) != 0) - { - std::vector vopretNonfungible; - GetNonfungibleData(assetid, vopretNonfungible); - - askamount = vintx.vout[0].nValue; - mtx.vin.push_back(CTxIn(asktxid, 0, CScript())); - - if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) - { - if (funcid == 's') - mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s' - else if (funcid=='S') - mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // marker if funcid='S' - } - - if (vopretNonfungible.size() > 0) - cpAssets->evalcodeNFT = vopretNonfungible.begin()[0]; - - mtx.vout.push_back(MakeTokensCC1vout(cpAssets->evalcodeNFT == 0 ? EVAL_TOKENS : cpAssets->evalcodeNFT, askamount, mypk)); // one-eval token vout - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(mypk); - - // this is only for unspendable addresses: - //CCaddr2set(cpTokens, EVAL_ASSETS, mypk, myPrivkey, myCCaddr); //do we need this? Seems FinalizeCCTx can attach to any evalcode cc addr by calling Getscriptaddress - - uint8_t unspendableAssetsPrivkey[32]; - char unspendableAssetsAddr[64]; - // init assets 'unspendable' privkey and pubkey - CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); - GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk); - - // add additional eval-tokens unspendable assets privkey: - CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); - - return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, - std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('x', zeroid, 0, Mypubkey()))))); - } - } - return(""); -} - -//send tokens, receive coins: -std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t fillamount) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction vintx; - uint256 hashBlock; - CPubKey mypk; - std::vector origpubkey; - int32_t bidvout=0; - uint64_t mask; - int64_t origprice, bidamount, paid_amount, remaining_required, inputs, CCchange=0; - struct CCcontract_info *cpTokens, tokensC; - struct CCcontract_info *cpAssets, assetsC; - - if (fillamount < 0) - { - fprintf(stderr,"negative fillamount %lld\n", (long long)fillamount); - return(""); - } - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - - if (txfee == 0) - txfee = 10000; - - mypk = pubkey2pk(Mypubkey()); - - if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) - { - mask = ~((1LL << mtx.vin.size()) - 1); - if (myGetTransaction(bidtxid, vintx, hashBlock) != 0) - { - bidamount = vintx.vout[bidvout].nValue; - SetAssetOrigpubkey(origpubkey, origprice, vintx); - - mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); // Coins on Assets unspendable - - std::vector vopretNonfungible; - if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60, vopretNonfungible)) > 0) - { - if (inputs < fillamount) { - std::cerr << "FillBuyOffer(): insufficient tokens to fill buy offer" << std::endl; - CCerror = strprintf("insufficient tokens to fill buy offer"); - return (""); - } - - SetBidFillamounts(paid_amount, remaining_required, bidamount, fillamount, origprice); - - uint8_t additionalTokensEvalcode2 = 0; - if (vopretNonfungible.size() > 0) - additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; - - if (inputs > fillamount) - CCchange = (inputs - fillamount); - - uint8_t unspendableAssetsPrivkey[32]; - cpAssets = CCinit(&assetsC, EVAL_ASSETS); - CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); - - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder - mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // vout1 coins to normal - mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, fillamount, pubkey2pk(origpubkey))); // vout2 single-eval tokens sent to the originator - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, origpubkey)); // vout3 marker to origpubkey - - if (CCchange != 0) - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // vout4 change in single-eval tokens - - fprintf(stderr,"FillBuyOffer() remaining %llu -> origpubkey\n", (long long)remaining_required); - - char unspendableAssetsAddr[64]; - cpAssets = CCinit(&assetsC, EVAL_ASSETS); - GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk); - - // add additional unspendable addr from Assets: - CCaddr2set(cpTokens, EVAL_ASSETS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); - - // token vout verification pubkeys: - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(pubkey2pk(origpubkey)); - - return(FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, - std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey))))); - } else return("dont have any assets to fill bid"); - } - } - return("no normal coins left"); -} - - -// send coins, receive tokens -std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 asktxid, int64_t fillunits) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction vintx,filltx; - uint256 hashBlock; - CPubKey mypk; - std::vector origpubkey; - double dprice; - uint64_t mask = 0; - int32_t askvout = 0; - int64_t received_assetoshis, total_nValue, orig_assetoshis, paid_nValue, remaining_nValue, inputs, CCchange=0; - //struct CCcontract_info *cpTokens, tokensC; - struct CCcontract_info *cpAssets, assetsC; - - if (fillunits < 0) - { - CCerror = strprintf("negative fillunits %lld\n",(long long)fillunits); - fprintf(stderr,"%s\n",CCerror.c_str()); - return(""); - } - if (assetid2 != zeroid) - { - CCerror = "asset swaps disabled"; - fprintf(stderr,"%s\n",CCerror.c_str()); - return(""); - } - - std::vector vopretNonfungible; - uint8_t additionalTokensEvalcode2 = 0; - GetNonfungibleData(assetid, vopretNonfungible); - if (vopretNonfungible.size() > 0) - additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; - - cpAssets = CCinit(&assetsC, EVAL_ASSETS); - - if (txfee == 0) - txfee = 10000; - - mypk = pubkey2pk(Mypubkey()); - //if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) - //{ - //mask = ~((1LL << mtx.vin.size()) - 1); - if (myGetTransaction(asktxid, vintx, hashBlock) != 0) - { - orig_assetoshis = vintx.vout[askvout].nValue; - SetAssetOrigpubkey(origpubkey, total_nValue, vintx); - dprice = (double)total_nValue / orig_assetoshis; - paid_nValue = dprice * fillunits; - - if (assetid2 != zeroid) { - inputs = 0; // = AddAssetInputs(cpAssets, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet - } - else - { - inputs = AddNormalinputs(mtx, mypk, 2 * txfee + paid_nValue, 60); // Better to use single AddNormalinputs() to allow payment if user has only single utxo with normal funds - mask = ~((1LL << mtx.vin.size()) - 1); - } - if (inputs > 0) - { - if (inputs < paid_nValue) { - std::cerr << "FillSell(): insufficient coins to fill sell" << std::endl; - CCerror = strprintf("insufficient coins to fill sell"); - return (""); - } - - // cc vin should be after normal vin - mtx.vin.push_back(CTxIn(asktxid, askvout, CScript())); - - if (assetid2 != zeroid) - SetSwapFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); //not implemented correctly yet - else - SetAskFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); - - if (assetid2 != zeroid && inputs > paid_nValue) - CCchange = (inputs - paid_nValue); - - // vout.0 tokens remainder to unspendable cc addr: - mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, additionalTokensEvalcode2, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL))); - //vout.1 purchased tokens to self token single-eval or dual-eval token+nonfungible cc addr: - mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, received_assetoshis, mypk)); - - if (assetid2 != zeroid) { - std::cerr << "FillSell() WARNING: asset swap not implemented yet! (paid_nValue)" << std::endl; - // TODO: change MakeCC1vout appropriately when implementing: - //mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, paid_nValue, origpubkey)); //vout.2 tokens... (swap is not implemented yet) - } - else { - //std::cerr << "FillSell() paid_value=" << paid_nValue << " origpubkey=" << HexStr(pubkey2pk(origpubkey)) << std::endl; - mtx.vout.push_back(CTxOut(paid_nValue, CScript() << origpubkey << OP_CHECKSIG)); //vout.2 coins to tokens seller's normal addr - } - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,txfee,origpubkey)); //vout.3 marker to origpubkey - - // not implemented - if (CCchange != 0) { - std::cerr << "FillSell() WARNING: asset swap not implemented yet! (CCchange)" << std::endl; - // TODO: change MakeCC1vout appropriately when implementing: - //mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); //vout.3 coins in Assets cc addr (swap not implemented) - } - - uint8_t unspendableAssetsPrivkey[32]; - char unspendableAssetsAddr[64]; - // init assets 'unspendable' privkey and pubkey - CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); - GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk); - - // add additional eval-tokens unspendable assets privkey: - CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); - - // vout verification pubkeys: - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(mypk); - - cpAssets->evalcodeNFT = additionalTokensEvalcode2; - - return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, - std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey))))); - } else { - CCerror = strprintf("filltx not enough utxos"); - fprintf(stderr,"%s\n", CCerror.c_str()); - } - } - //} - return(""); -} - -}; \ No newline at end of file diff --git a/src/cc/old/CCtokens_v0.cpp b/src/cc/old/CCtokens_v0.cpp deleted file mode 100644 index 831be452c1d..00000000000 --- a/src/cc/old/CCtokens_v0.cpp +++ /dev/null @@ -1,1059 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include "CCtokens_v0.h" -#include "importcoin.h" - -namespace tokensv0 { - -/* TODO: correct this: ------------------------------ - The SetTokenFillamounts() and ValidateTokenRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively. - - This pair of functions are critical to make sure the trading is correct and is the trickiest part of the tokens contract. - - //vin.0: normal input - //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0] - //vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue - //vout.0: remaining amount of bid to unspendable - //vout.1: vin.1 value to signer of vin.2 - //vout.2: vin.2 tokenoshis to original pubkey - //vout.3: CC output for tokenoshis change (if any) - //vout.4: normal output for change (if any) - //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [tokenid] [remaining token required] [origpubkey] - ValidateTokenRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits); - - Yes, this is quite confusing... - - In ValidateTokenRemainder the naming convention is nValue is the coin/token with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or tokens, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits. - - We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it. - ------------------------------ -*/ - -// extract cc token vins' pubkeys: -static bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector &vinPubkeys) { - - bool found = false; - CPubKey pubkey; - struct CCcontract_info *cpTokens, tokensC; - - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - vinPubkeys.clear(); - - for (int32_t i = 0; i < tx.vin.size(); i++) - { - // check for cc token vins: - if( (*cpTokens->ismyvin)(tx.vin[i].scriptSig) ) - { - - auto findEval = [](CC *cond, struct CCVisitor _) { - bool r = false; - - if (cc_typeId(cond) == CC_Secp256k1) { - *(CPubKey*)_.context = buf2pk(cond->publicKey); - //std::cerr << "findEval found pubkey=" << HexStr(*(CPubKey*)_.context) << std::endl; - r = true; - } - // false for a match, true for continue - return r ? 0 : 1; - }; - - CC *cond = GetCryptoCondition(tx.vin[i].scriptSig); - - if (cond) { - CCVisitor visitor = { findEval, (uint8_t*)"", 0, &pubkey }; - bool out = !cc_visit(cond, visitor); - cc_free(cond); - - if (pubkey.IsValid()) { - vinPubkeys.push_back(pubkey); - found = true; - } - } - } - } - return found; -} - -// tx validation -bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) -{ - static uint256 zero; - CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2; - int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts; - int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore; - std::vector> oprets; - vscript_t /*vopretExtra,*/ tmporigpubkey, ignorepubkey; - uint8_t funcid, evalCodeInOpret; - char destaddr[64], origaddr[64], CCaddr[64]; - std::vector voutTokenPubkeys, vinTokenPubkeys; - - if (strcmp(ASSETCHAINS_SYMBOL, "ROGUE") == 0 && chainActive.Height() <= 12500) - return true; - - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - outputs = inputs = 0; - preventCCvins = preventCCvouts = -1; - - // check boundaries: - if (numvouts < 1) - return eval->Invalid("no vouts"); - - if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets)) == 0) - return eval->Invalid("TokenValidate: invalid opreturn payload"); - - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokensValidate funcId=" << (char)(funcid?funcid:' ') << " evalcode=" << std::hex << (int)cp->evalcode << std::endl); - - if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0) - return eval->Invalid("cant find token create txid"); - //else if (IsCCInput(tx.vin[0].scriptSig) != 0) - // return eval->Invalid("illegal token vin0"); // <-- this validation was removed because some token tx might not have normal vins - else if (funcid != 'c') - { - if (tokenid == zeroid) - return eval->Invalid("illegal tokenid"); - else if (!TokensExactAmounts(true, cp, inputs, outputs, eval, tx, tokenid)) { - if (!eval->Valid()) - return false; //TokenExactAmounts must call eval->Invalid()! - else - return eval->Invalid("tokens cc inputs != cc outputs"); - } - } - - // validate spending from token cc addr: allowed only for burned non-fungible tokens: - if (ExtractTokensCCVinPubkeys(tx, vinTokenPubkeys) && std::find(vinTokenPubkeys.begin(), vinTokenPubkeys.end(), GetUnspendable(cp, NULL)) != vinTokenPubkeys.end()) { - // validate spending from token unspendable cc addr: - int64_t burnedAmount = HasBurnedTokensvouts(cp, eval, tx, tokenid); - if (burnedAmount > 0) { - vscript_t vopretNonfungible; - GetNonfungibleData(tokenid, vopretNonfungible); - if( vopretNonfungible.empty() ) - return eval->Invalid("spending cc marker not supported for fungible tokens"); - } - } - - switch (funcid) - { - case 'c': // token create should not be validated as it has no CC inputs, so return 'invalid' - // token tx structure for 'c': - //vin.0: normal input - //vout.0: issuance tokenoshis to CC - //vout.1: normal output for change (if any) - //vout.n-1: opreturn EVAL_TOKENS 'c' - return eval->Invalid("incorrect token funcid"); - - case 't': // transfer - // token tx structure for 't' - //vin.0: normal input - //vin.1 .. vin.n-1: valid CC outputs - //vout.0 to n-2: tokenoshis output to CC - //vout.n-2: normal output for change (if any) - //vout.n-1: opreturn EVAL_TOKENS 't' tokenid - if (inputs == 0) - return eval->Invalid("no token inputs for transfer"); - - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "token transfer preliminarily validated inputs=" << inputs << "->outputs=" << outputs << " preventCCvins=" << preventCCvins<< " preventCCvouts=" << preventCCvouts << std::endl); - break; // breaking to other contract validation... - - default: - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "illegal tokens funcid=" << (char)(funcid?funcid:' ') << std::endl); - return eval->Invalid("unexpected token funcid"); - } - - return true; -} - -// helper funcs: - - -// this is just for log messages indentation fur debugging recursive calls: -thread_local uint32_t tokenValIndentSize = 0; - -// validates opret for token tx: -uint8_t ValidateTokenOpret(CTransaction tx, uint256 tokenid) { - - uint256 tokenidOpret = zeroid; - uint8_t funcid; - uint8_t dummyEvalCode; - std::vector voutPubkeysDummy; - std::vector> opretsDummy; - - // this is just for log messages indentation fur debugging recursive calls: - std::string indentStr = std::string().append(tokenValIndentSize, '.'); - - if (tx.vout.size() == 0) - return (uint8_t)0; - - if ((funcid = DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeysDummy, opretsDummy)) == 0) - { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl); - return (uint8_t)0; - } - else if (funcid == 'c') - { - if (tokenid != zeroid && tokenid == tx.GetHash()) { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); - return funcid; - } - else { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenbase txid=" << tx.GetHash().GetHex() << std::endl); - } - } - else if (funcid == 'i') - { - if (tokenid != zeroid && tokenid == tx.GetHash()) { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is import 'i' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); - return funcid; - } - else { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my import txid=" << tx.GetHash().GetHex() << std::endl); - } - } - else if (funcid == 't') - { - //std::cerr << indentStr << "ValidateTokenOpret() tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; - if (tokenid != zeroid && tokenid == tokenidOpret) { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); - return funcid; - } - else { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenid=" << tokenidOpret.GetHex() << std::endl); - } - } - else { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not supported funcid=" << (char)funcid << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl); - } - return (uint8_t)0; -} - -// remove token->unspendablePk (it is only for marker usage) -void FilterOutTokensUnspendablePk(const std::vector &sourcePubkeys, std::vector &destPubkeys) { - struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - CPubKey tokensUnspendablePk = GetUnspendable(cpTokens, NULL); - destPubkeys.clear(); - - for (auto pk : sourcePubkeys) - if (pk != tokensUnspendablePk) - destPubkeys.push_back(pk); - -} - -void FilterOutNonCCOprets(const std::vector> &oprets, vscript_t &vopret) { - - vopret.clear(); - - if (oprets.size() > 2) - LOGSTREAM("cctokens", CCLOG_INFO, stream << "FilterOutNonCCOprets() warning!! oprets.size > 2 currently not supported" << oprets.size() << std::endl); - - for (auto o : oprets) { - if (o.first < OPRETID_FIRSTNONCCDATA) { // skip burn, import, etc opret data - vopret = o.second; // return first contract opret (more than 1 is not supported yet) - break; - } - } -} - -// Checks if the vout is a really Tokens CC vout -// also checks tokenid in opret or txid if this is 'c' tx -// goDeeper is true: the func also validates amounts of the passed transaction: -// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx -// checkPubkeys is true: validates if the vout is token vout1 or token vout1of2. Should always be true! -int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true*/, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid) -{ - - // this is just for log messages indentation fur debugging recursive calls: - std::string indentStr = std::string().append(tokenValIndentSize, '.'); - - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl); - - int32_t n = tx.vout.size(); - // just check boundaries: - if (n == 0 || v < 0 || v >= n-1) { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "isTokensvout() incorrect params: (n == 0 or v < 0 or v >= n-1)" << " v=" << v << " n=" << n << " returning 0" << std::endl); - return(0); - } - - if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition()) - { - if (goDeeper) { - //validate all tx - int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0; - - tokenValIndentSize++; - // false --> because we already at the 1-st level ancestor tx and do not need to dereference ancestors of next levels - bool isEqual = TokensExactAmounts(false, cp, myCCVinsAmount, myCCVoutsAmount, eval, tx, reftokenid); - tokenValIndentSize--; - - if (!isEqual) { - // if ccInputs != ccOutputs and it is not the tokenbase tx - // this means it is possibly a fake tx (dimxy): - if (reftokenid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy) - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() warning: for the verified tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx" << std::endl); - return 0; - } - } - } - - // token opret most important checks (tokenid == reftokenid, tokenid is non-zero, tx is 'tokenbase'): - const uint8_t funcId = ValidateTokenOpret(tx, reftokenid); - //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - if (funcId != 0) { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null funcId=" << (char)(funcId ? funcId : ' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); - - uint8_t dummyEvalCode; - uint256 tokenIdOpret; - std::vector voutPubkeys, voutPubkeysInOpret; - vscript_t vopretExtra, vopretNonfungible; - std::vector> oprets; - - uint8_t evalCodeNonfungible = 0; - uint8_t evalCode1 = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysDeposit - uint8_t evalCode2 = 0; // will be checked if zero or not - - // test vouts for possible token use-cases: - std::vector> testVouts; - - DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeysInOpret, oprets); - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() oprets.size()=" << oprets.size() << std::endl); - - // get assets/channels/gateways token data: - FilterOutNonCCOprets(oprets, vopretExtra); // NOTE: only 1 additional evalcode in token opret is currently supported - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl); - - // get non-fungible data - GetNonfungibleData(reftokenid, vopretNonfungible); - FilterOutTokensUnspendablePk(voutPubkeysInOpret, voutPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there) - - // NOTE: evalcode order in vouts is important: - // non-fungible-eval -> EVAL_TOKENS -> assets-eval - - if (vopretNonfungible.size() > 0) - evalCodeNonfungible = evalCode1 = vopretNonfungible.begin()[0]; - if (vopretExtra.size() > 0) - evalCode2 = vopretExtra.begin()[0]; - - if (evalCode1 == EVAL_TOKENS && evalCode2 != 0) { - evalCode1 = evalCode2; // for using MakeTokensCC1vout(evalcode,...) instead of MakeCC1vout(EVAL_TOKENS, evalcode...) - evalCode2 = 0; - } - - if( /*checkPubkeys &&*/ funcId != 'c' ) { // for 'c' there is no pubkeys - // verify that the vout is token by constructing vouts with the pubkeys in the opret: - - // maybe this is dual-eval 1 pubkey or 1of2 pubkey vout? - if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) { - // check dual/three-eval 1 pubkey vout with the first pubkey - testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0]")) ); - if (evalCode2 != 0) - // also check in backward evalcode order - testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0] backward-eval")) ); - - if(voutPubkeys.size() == 2) { - // check dual/three eval 1of2 pubkeys vout - testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2")) ); - // check dual/three eval 1 pubkey vout with the second pubkey - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1]"))); - if (evalCode2 != 0) { - // also check in backward evalcode order: - // check dual/three eval 1of2 pubkeys vout - testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2 backward-eval"))); - // check dual/three eval 1 pubkey vout with the second pubkey - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1] backward-eval"))); - } - } - - // maybe this is like gatewayclaim to single-eval token? - if( evalCodeNonfungible == 0 ) // do not allow to convert non-fungible to fungible token - testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]), std::string("single-eval cc1 pk[0]"))); - - // maybe this is like FillSell for non-fungible token? - if( evalCode1 != 0 ) - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval-token cc1 pk[0]"))); - if( evalCode2 != 0 ) - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval2-token cc1 pk[0]"))); - - // the same for pk[1]: - if (voutPubkeys.size() == 2) { - if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token - testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]), std::string("single-eval cc1 pk[1]"))); - if (evalCode1 != 0) - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval-token cc1 pk[1]"))); - if (evalCode2 != 0) - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval2-token cc1 pk[1]"))); - } - - //special check for tx when spending from 1of2 CC address and one of pubkeys is global CC pubkey - struct CCcontract_info *cpEvalCode1,CEvalCode1; - cpEvalCode1 = CCinit(&CEvalCode1,evalCode1); - CPubKey pk=GetUnspendable(cpEvalCode1,0); - testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval1 pegscc cc1of2 pk[0] globalccpk")) ); - if (voutPubkeys.size() == 2) testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval1 pegscc cc1of2 pk[1] globalccpk")) ); - if (evalCode2!=0) - { - struct CCcontract_info *cpEvalCode2,CEvalCode2; - cpEvalCode2 = CCinit(&CEvalCode2,evalCode2); - CPubKey pk=GetUnspendable(cpEvalCode2,0); - testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval2 pegscc cc1of2 pk[0] globalccpk")) ); - if (voutPubkeys.size() == 2) testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval2 pegscc cc1of2 pk[1] globalccpk")) ); - } - } - - // maybe it is single-eval or dual/three-eval token change? - std::vector vinPubkeys, vinPubkeysUnfiltered; - ExtractTokensCCVinPubkeys(tx, vinPubkeysUnfiltered); - FilterOutTokensUnspendablePk(vinPubkeysUnfiltered, vinPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there) - - for(std::vector::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) { - if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token - testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it), std::string("single-eval cc1 self vin pk"))); - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk"))); - - if (evalCode2 != 0) - // also check in backward evalcode order: - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk backward-eval"))); - } - - // try all test vouts: - for (auto t : testVouts) { - if (t.first == tx.vout[v]) { // test vout matches - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() valid amount=" << tx.vout[v].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl); - return tx.vout[v].nValue; - } - } - - } - else { // funcid == 'c' - - if (!tx.IsCoinImport()) { - - vscript_t vorigPubkey; - std::string dummyName, dummyDescription; - std::vector> oprets; - - if (DecodeTokenCreateOpRet(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, oprets) == 0) { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() could not decode create opret" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); - return 0; - } - - CPubKey origPubkey = pubkey2pk(vorigPubkey); - - - // TODO: add voutPubkeys for 'c' tx - - /* this would not work for imported tokens: - // for 'c' recognize the tokens only to token originator pubkey (but not to unspendable <-- closed sec violation) - // maybe this is like gatewayclaim to single-eval token? - if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token - testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, origPubkey), std::string("single-eval cc1 orig-pk"))); - // maybe this is like FillSell for non-fungible token? - if (evalCode1 != 0) - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, origPubkey), std::string("dual-eval-token cc1 orig-pk"))); */ - - // note: this would not work if there are several pubkeys in the tokencreator's wallet (AddNormalinputs does not use pubkey param): - // for tokenbase tx check that normal inputs sent from origpubkey > cc outputs - int64_t ccOutputs = 0; - for (auto vout : tx.vout) - if (vout.scriptPubKey.IsPayToCryptoCondition() //TODO: add voutPubkey validation - && !IsTokenMarkerVout(vout)) // should not be marker here - ccOutputs += vout.nValue; - - int64_t normalInputs = TotalPubkeyNormalInputs(tx, origPubkey); // check if normal inputs are really signed by originator pubkey (someone not cheating with originator pubkey) - LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << " for tokenbase=" << reftokenid.GetHex() << std::endl); - - if (normalInputs >= ccOutputs) { - LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() assured normalInputs >= ccOutputs" << " for tokenbase=" << reftokenid.GetHex() << std::endl); - if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker - return tx.vout[v].nValue; - else - return 0; // vout is good, but do not take marker into account - } - else { - LOGSTREAM("cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() skipping vout not fulfilled normalInputs >= ccOutput" << " for tokenbase=" << reftokenid.GetHex() << " normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << std::endl); - } - } - else { - // imported tokens are checked in the eval::ImportCoin() validation code - if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker - return tx.vout[v].nValue; - else - return 0; // vout is good, but do not take marker into account - } - } - LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() no valid vouts evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); - } - //std::cerr << indentStr; fprintf(stderr,"IsTokensvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str()); - } - //std::cerr << indentStr; fprintf(stderr,"IsTokensvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN); - return(0); -} - -bool IsTokenMarkerVout(CTxOut vout) { - struct CCcontract_info *cpTokens, CCtokens_info; - cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS); - return vout == MakeCC1vout(EVAL_TOKENS, vout.nValue, GetUnspendable(cpTokens, NULL)); -} - -// compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs) -bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 reftokenid) -{ - CTransaction vinTx; - uint256 hashBlock; - int64_t tokenoshis; - - struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - - int32_t numvins = tx.vin.size(); - int32_t numvouts = tx.vout.size(); - inputs = outputs = 0; - - // this is just for log messages indentation for debugging recursive calls: - std::string indentStr = std::string().append(tokenValIndentSize, '.'); - - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokensExactAmounts() entered for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); - - for (int32_t i = 0; iismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/) - { - //std::cerr << indentStr << "TokensExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl; - // we are not inside the validation code -- dimxy - if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock))) - { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl); - return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt"); - } - else { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() checking vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl); - - // validate vouts of vintx - tokenValIndentSize++; - tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, reftokenid); - tokenValIndentSize--; - if (tokenoshis != 0) - { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding vintx.vout for tx.vin[" << i << "] tokenoshis=" << tokenoshis << std::endl); - inputs += tokenoshis; - } - } - } - } - - for (int32_t i = 0; i < numvouts-1; i ++) // 'numvouts-1' <-- do not check opret - { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() recursively checking tx.vout[" << i << "] nValue=" << tx.vout[i].nValue << std::endl); - - // Note: we pass in here IsTokenvout(false,...) because we don't need to call TokenExactAmounts() recursively from IsTokensvout here - // indeed, if we pass 'true' we'll be checking this tx vout again - tokenValIndentSize++; - tokenoshis = IsTokensvout(false /*<--do not recursion here*/, true /*<--exclude non-tokens vouts*/, cpTokens, eval, tx, i, reftokenid); - tokenValIndentSize--; - - if (tokenoshis != 0) - { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding tx.vout[" << i << "] tokenoshis=" << tokenoshis << std::endl); - outputs += tokenoshis; - } - } - - //std::cerr << indentStr << "TokensExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; - - if (inputs != outputs) { - if (tx.GetHash() != reftokenid) - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokenExactAmounts() found unequal token cc inputs=" << inputs << " vs cc outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << " and this is not the create tx" << std::endl); - //fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); - return false; // do not call eval->Invalid() here! - } - else - return true; -} - - -// get non-fungible data from 'tokenbase' tx (the data might be empty) -void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible) -{ - CTransaction tokenbasetx; - uint256 hashBlock; - - if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "GetNonfungibleData() cound not load token creation tx=" << tokenid.GetHex() << std::endl); - return; - } - - vopretNonfungible.clear(); - // check if it is non-fungible tx and get its second evalcode from non-fungible payload - if (tokenbasetx.vout.size() > 0) { - std::vector origpubkey; - std::string name, description; - std::vector> oprets; - - if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets) == 'c') { - GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); - } - } -} - - -// overload, adds inputs from token cc addr -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs) { - vscript_t vopretNonfungibleDummy; - return AddTokenCCInputs(cp, mtx, pk, tokenid, total, maxinputs, vopretNonfungibleDummy); -} - -// adds inputs from token cc addr and returns non-fungible opret payload if present -// also sets evalcode in cp, if needed -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs, vscript_t &vopretNonfungible) -{ - char tokenaddr[64], destaddr[64]; - int64_t threshold, nValue, price, totalinputs = 0; - int32_t n = 0; - std::vector > unspentOutputs; - - GetNonfungibleData(tokenid, vopretNonfungible); - if (vopretNonfungible.size() > 0) - cp->evalcodeNFT = vopretNonfungible.begin()[0]; - - GetTokensCCaddress(cp, tokenaddr, pk); - SetCCunspents(unspentOutputs, tokenaddr,true); - - - if (unspentOutputs.empty()) { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->evalcodeNFT << std::endl); - } - - threshold = total / (maxinputs != 0 ? maxinputs : CC_MAXVINS); - - for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) - { - CTransaction vintx; - uint256 hashBlock; - uint256 vintxid = it->first.txhash; - int32_t vout = (int32_t)it->first.index; - - if (it->second.satoshis < threshold) // this should work also for non-fungible tokens (there should be only 1 satoshi for non-fungible token issue) - continue; - - int32_t ivin; - for (ivin = 0; ivin < mtx.vin.size(); ivin ++) - if (vintxid == mtx.vin[ivin].prevout.hash && vout == mtx.vin[ivin].prevout.n) - break; - if (ivin != mtx.vin.size()) // that is, the tx.vout is already added to mtx.vin (in some previous calls) - continue; - - if (myGetTransaction(vintxid, vintx, hashBlock) != 0) - { - Getscriptaddress(destaddr, vintx.vout[vout].scriptPubKey); - if (strcmp(destaddr, tokenaddr) != 0 && - strcmp(destaddr, cp->unspendableCCaddr) != 0 && // TODO: check why this. Should not we add token inputs from unspendable cc addr if mypubkey is used? - strcmp(destaddr, cp->unspendableaddr2) != 0) // or the logic is to allow to spend all available tokens (what about unspendableaddr3)? - continue; - - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() check vintx vout destaddress=" << destaddr << " amount=" << vintx.vout[vout].nValue << std::endl); - - if ((nValue = IsTokensvout(true, true/*<--add only valid token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,vintxid, vout) == 0) - { - //for non-fungible tokens check payload: - if (!vopretNonfungible.empty()) { - vscript_t vopret; - - // check if it is non-fungible token: - GetNonfungibleData(tokenid, vopret); - if (vopret != vopretNonfungible) { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() found incorrect non-fungible opret payload for vintxid=" << vintxid.GetHex() << std::endl); - continue; - } - // non-fungible evalCode2 cc contract should also check if there exists only one non-fungible vout with amount = 1 - } - - - if (total != 0 && maxinputs != 0) // if it is not just to calc amount... - mtx.vin.push_back(CTxIn(vintxid, vout, CScript())); - - nValue = it->second.satoshis; - totalinputs += nValue; - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() adding input nValue=" << nValue << std::endl); - n++; - - if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) - break; - } - } - } - - //std::cerr << "AddTokenCCInputs() found totalinputs=" << totalinputs << std::endl; - return(totalinputs); -} - -// checks if any token vouts are sent to 'dead' pubkey -int64_t HasBurnedTokensvouts(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, uint256 reftokenid) -{ - uint8_t dummyEvalCode; - uint256 tokenIdOpret; - std::vector voutPubkeys, voutPubkeysDummy; - std::vector> oprets; - vscript_t vopretExtra, vopretNonfungible; - - uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysDepost - uint8_t evalCode2 = 0; // will be checked if zero or not - - // test vouts for possible token use-cases: - std::vector> testVouts; - - int32_t n = tx.vout.size(); - // just check boundaries: - if (n == 0) { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "HasBurnedTokensvouts() incorrect params: tx.vout.size() == 0, txid=" << tx.GetHash().GetHex() << std::endl); - return(0); - } - - - if (DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeysDummy, oprets) == 0) { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "HasBurnedTokensvouts() cannot parse opret DecodeTokenOpRet returned 0, txid=" << tx.GetHash().GetHex() << std::endl); - return 0; - } - - // get assets/channels/gateways token data: - FilterOutNonCCOprets(oprets, vopretExtra); // NOTE: only 1 additional evalcode in token opret is currently supported - - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "HasBurnedTokensvouts() vopretExtra=" << HexStr(vopretExtra) << std::endl); - - GetNonfungibleData(reftokenid, vopretNonfungible); - - if (vopretNonfungible.size() > 0) - evalCode = vopretNonfungible.begin()[0]; - if (vopretExtra.size() > 0) - evalCode2 = vopretExtra.begin()[0]; - - if (evalCode == EVAL_TOKENS && evalCode2 != 0) { - evalCode = evalCode2; - evalCode2 = 0; - } - - voutPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY))); - - int64_t burnedAmount = 0; - - for (int i = 0; i < tx.vout.size(); i++) { - - if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition()) - { - // make all possible token vouts for dead pk: - for (std::vector::iterator it = voutPubkeys.begin(); it != voutPubkeys.end(); it++) { - testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[i].nValue, *it), std::string("single-eval cc1 burn pk"))); - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[i].nValue, *it), std::string("three-eval cc1 burn pk"))); - - if (evalCode2 != 0) - // also check in backward evalcode order: - testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[i].nValue, *it), std::string("three-eval cc1 burn pk backward-eval"))); - } - - // try all test vouts: - for (auto t : testVouts) { - if (t.first == tx.vout[i]) { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "HasBurnedTokensvouts() burned amount=" << tx.vout[i].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl); - burnedAmount += tx.vout[i].nValue; - } - } - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "HasBurnedTokensvouts() total burned=" << burnedAmount << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); - } - } - - return burnedAmount; -} - -CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey) { - - uint8_t funcId, evalCode; - uint256 tokenid; - std::vector voutTokenPubkeys; - std::vector> oprets; - - if ((funcId = DecodeTokenOpRet(scriptPubKey, evalCode, tokenid, voutTokenPubkeys, oprets)) != 0) { - CTransaction tokenbasetx; - uint256 hashBlock; - - if (myGetTransaction(tokenid, tokenbasetx, hashBlock) && tokenbasetx.vout.size() > 0) { - vscript_t vorigpubkey; - std::string name, desc; - if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, desc) != 0) - return pubkey2pk(vorigpubkey); - } - } - return CPubKey(); //return invalid pubkey -} - -// returns token creation signed raw tx -std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, std::string description, vscript_t nonfungibleData) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk; struct CCcontract_info *cp, C; - if (tokensupply < 0) { - CCerror = "negative tokensupply"; - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() =" << CCerror << "=" << tokensupply << std::endl); - return std::string(""); - } - if (!nonfungibleData.empty() && tokensupply != 1) { - CCerror = "for non-fungible tokens tokensupply should be equal to 1"; - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() " << CCerror << std::endl); - return std::string(""); - } - - - cp = CCinit(&C, EVAL_TOKENS); - if (name.size() > 32 || description.size() > 4096) // this is also checked on rpc level - { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "name len=" << name.size() << " or description len=" << description.size() << " is too big" << std::endl); - CCerror = "name should be <= 32, description should be <= 4096"; - return(""); - } - if (txfee == 0) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - - if (AddNormalinputs2(mtx, tokensupply + 2 * txfee, 64) > 0) // add normal inputs only from mypk - { - int64_t mypkInputs = TotalPubkeyNormalInputs(mtx, mypk); - if (mypkInputs < tokensupply) { // check that tokens amount are really issued with mypk (because in the wallet there maybe other privkeys) - CCerror = "some inputs signed not with -pubkey=pk"; - return std::string(""); - } - - uint8_t destEvalCode = EVAL_TOKENS; - if( nonfungibleData.size() > 0 ) - destEvalCode = nonfungibleData.begin()[0]; - - // NOTE: we should prevent spending fake-tokens from this marker in IsTokenvout(): - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // new marker to token cc addr, burnable and validated, vout pos now changed to 0 (from 1) - mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, tokensupply, mypk)); - //mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); // old marker (non-burnable because spending could not be validated) - //mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // ...moved to vout=0 for matching with rogue-game token - - return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description, nonfungibleData))); - } - - CCerror = "cant find normal inputs"; - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() " << CCerror << std::endl); - return std::string(""); -} - -// transfer tokens to another pubkey -// param additionalEvalCode allows transfer of dual-eval non-fungible tokens -std::string TokenTransfer(int64_t txfee, uint256 tokenid, vscript_t destpubkey, int64_t total) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk; uint64_t mask; int64_t CCchange = 0, inputs = 0; struct CCcontract_info *cp, C; - vscript_t vopretNonfungible, vopretEmpty; - - if (total < 0) { - CCerror = strprintf("negative total"); - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << CCerror << "=" << total << std::endl); - return(""); - } - - cp = CCinit(&C, EVAL_TOKENS); - - if (txfee == 0) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - /*if ( cp->tokens1of2addr[0] == 0 ) - { - GetTokensCCaddress(cp, cp->tokens1of2addr, mypk); - fprintf(stderr,"set tokens1of2addr <- %s\n",cp->tokens1of2addr); - }*/ - if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) - { - mask = ~((1LL << mtx.vin.size()) - 1); // seems, mask is not used anymore - - if ((inputs = AddTokenCCInputs(cp, mtx, mypk, tokenid, total, 60, vopretNonfungible)) > 0) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx! - { - if (inputs < total) { //added dimxy - CCerror = strprintf("insufficient token inputs"); - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << std::endl); - return std::string(""); - } - - uint8_t destEvalCode = EVAL_TOKENS; - if (vopretNonfungible.size() > 0) - destEvalCode = vopretNonfungible.begin()[0]; - - if (inputs > total) - CCchange = (inputs - total); - mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, total, pubkey2pk(destpubkey))); // if destEvalCode == EVAL_TOKENS then it is actually MakeCC1vout(EVAL_TOKENS,...) - if (CCchange != 0) - mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, CCchange, mypk)); - - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(pubkey2pk(destpubkey)); // dest pubkey for validating vout - - return FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, std::make_pair((uint8_t)0, vopretEmpty))); - } - else { - CCerror = strprintf("no token inputs"); - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << " for amount=" << total << std::endl); - } - //} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size()); - } - else { - CCerror = strprintf("insufficient normal inputs for tx fee"); - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << std::endl); - } - return(""); -} - - -int64_t GetTokenBalance(CPubKey pk, uint256 tokenid) -{ - uint256 hashBlock; - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction tokentx; - - // CCerror = strprintf("obsolete, cannot return correct value without eval"); - // return 0; - - if (myGetTransaction(tokenid, tokentx, hashBlock) == 0) - { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "cant find tokenid" << std::endl); - CCerror = strprintf("cant find tokenid"); - return 0; - } - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_TOKENS); - return(AddTokenCCInputs(cp, mtx, pk, tokenid, 0, 0)); -} - -UniValue TokenInfo(uint256 tokenid) -{ - UniValue result(UniValue::VOBJ); - uint256 hashBlock; - CTransaction tokenbaseTx; - std::vector origpubkey; - std::vector> oprets; - vscript_t vopretNonfungible; - std::string name, description; - struct CCcontract_info *cpTokens, tokensCCinfo; - - cpTokens = CCinit(&tokensCCinfo, EVAL_TOKENS); - - if( !myGetTransaction(tokenid, tokenbaseTx, hashBlock) ) - { - fprintf(stderr, "TokenInfo() cant find tokenid\n"); - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "cant find tokenid")); - return(result); - } - if ( KOMODO_NSPV_FULLNODE && hashBlock.IsNull()) { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "the transaction is still in mempool")); - return(result); - } - - if (tokenbaseTx.vout.size() > 0 && DecodeTokenCreateOpRet(tokenbaseTx.vout[tokenbaseTx.vout.size() - 1].scriptPubKey, origpubkey, name, description, oprets) != 'c') - { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenInfo() passed tokenid isnt token creation txid" << std::endl); - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "tokenid isnt token creation txid")); - return result; - } - result.push_back(Pair("result", "success")); - result.push_back(Pair("tokenid", tokenid.GetHex())); - result.push_back(Pair("owner", HexStr(origpubkey))); - result.push_back(Pair("name", name)); - - int64_t supply = 0, output; - for (int v = 0; v < tokenbaseTx.vout.size() - 1; v++) - if ((output = IsTokensvout(false, true, cpTokens, NULL, tokenbaseTx, v, tokenid)) > 0) - supply += output; - result.push_back(Pair("supply", supply)); - result.push_back(Pair("description", description)); - - GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); - if( !vopretNonfungible.empty() ) - result.push_back(Pair("data", HexStr(vopretNonfungible))); - - if (tokenbaseTx.IsCoinImport()) { // if imported token - ImportProof proof; - CTransaction burnTx; - std::vector payouts; - CTxDestination importaddress; - - std::string sourceSymbol = "can't decode"; - std::string sourceTokenId = "can't decode"; - - if (UnmarshalImportTx(tokenbaseTx, proof, burnTx, payouts)) - { - // extract op_return to get burn source chain. - std::vector burnOpret; - std::string targetSymbol; - uint32_t targetCCid; - uint256 payoutsHash; - std::vector rawproof; - if (UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash, rawproof)) { - if (rawproof.size() > 0) { - CTransaction tokenbasetx; - E_UNMARSHAL(rawproof, ss >> sourceSymbol; - if (!ss.eof()) - ss >> tokenbasetx); - - if (!tokenbasetx.IsNull()) - sourceTokenId = tokenbasetx.GetHash().GetHex(); - } - } - } - result.push_back(Pair("IsImported", "yes")); - result.push_back(Pair("sourceChain", sourceSymbol)); - result.push_back(Pair("sourceTokenId", sourceTokenId)); - } - - return result; -} - -UniValue TokenList() -{ - UniValue result(UniValue::VARR); - std::vector txids; - std::vector > addressIndexCCMarker; - - struct CCcontract_info *cp, C; uint256 txid, hashBlock; - CTransaction vintx; std::vector origpubkey; - std::string name, description; - - cp = CCinit(&C, EVAL_TOKENS); - - auto addTokenId = [&](uint256 txid) { - if (myGetTransaction(txid, vintx, hashBlock) != 0) { - if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0) { - result.push_back(txid.GetHex()); - } - } - }; - - SetCCtxids(txids, cp->normaladdr,false,cp->evalcode,10000,zeroid,'c'); // find by old normal addr marker - for (std::vector::const_iterator it = txids.begin(); it != txids.end(); it++) { - addTokenId(*it); - } - - SetCCunspents(addressIndexCCMarker, cp->unspendableCCaddr,true); // find by burnable validated cc addr marker - for (std::vector >::const_iterator it = addressIndexCCMarker.begin(); it != addressIndexCCMarker.end(); it++) { - addTokenId(it->first.txhash); - } - - return(result); -} - -}; \ No newline at end of file diff --git a/src/cc/old/CCtokens_v0.h b/src/cc/old/CCtokens_v0.h deleted file mode 100644 index 2738a680b70..00000000000 --- a/src/cc/old/CCtokens_v0.h +++ /dev/null @@ -1,99 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2018 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - - -/* - old code for compatibility with tokens cc version 0 - */ - -#ifndef CC_TOKENS_V0_H -#define CC_TOKENS_V0_H - -#include "../CCinclude.h" - -namespace tokensv0 { - -/// identifiers of additional data blobs in token opreturn script: -/// @see EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector> oprets) -/// @see GetOpretBlob -enum opretid : uint8_t { - // cc contracts data: - OPRETID_NONFUNGIBLEDATA = 0x11, //!< NFT data id - OPRETID_ASSETSDATA = 0x12, //!< assets contract data id - OPRETID_GATEWAYSDATA = 0x13, //!< gateways contract data id - OPRETID_CHANNELSDATA = 0x14, //!< channels contract data id - OPRETID_HEIRDATA = 0x15, //!< heir contract data id - OPRETID_ROGUEGAMEDATA = 0x16, //!< rogue contract data id - OPRETID_PEGSDATA = 0x17, //!< pegs contract data id - - /*! \cond INTERNAL */ - // non cc contract data: - OPRETID_FIRSTNONCCDATA = 0x80, - /*! \endcond */ - OPRETID_BURNDATA = 0x80, //!< burned token data id - OPRETID_IMPORTDATA = 0x81 //!< imported token data id -}; - -/// finds opret blob data by opretid in the vector of oprets -/// @param oprets vector of oprets -/// @param id opret id to search -/// @param vopret found opret blob as byte array -/// @returns true if found -/// @see opretid -inline bool GetOpretBlob(const std::vector>> &oprets, uint8_t id, std::vector &vopret) { - vopret.clear(); - for(auto p : oprets) if (p.first == id) { vopret = p.second; return true; } - return false; -} - -CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, vscript_t vopretNonfungible); -CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector> oprets); -uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description); -uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector> &oprets); -CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::pair opretWithId); -CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::vector> oprets); -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector> &oprets); -CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk); -CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk); -CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2); -CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2); -CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk); -CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk); -CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2); -CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2); -//bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk); -//bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2); -//void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, uint8_t *priv, char *coinaddr); - -// CCcustom -bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); -bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid); -std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description, std::vector nonfungibleData); -std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector destpubkey, int64_t total); -int64_t HasBurnedTokensvouts(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, uint256 reftokenid); -CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey); -int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid); -bool IsTokenMarkerVout(CTxOut vout); -void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible); -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs); -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs, vscript_t &vopretNonfungible); - -int64_t GetTokenBalance(CPubKey pk, uint256 tokenid); -UniValue TokenInfo(uint256 tokenid); -UniValue TokenList(); - -}; - -#endif diff --git a/src/cc/old/CCtokenutils_v0.cpp b/src/cc/old/CCtokenutils_v0.cpp deleted file mode 100644 index cbf2627759b..00000000000 --- a/src/cc/old/CCtokenutils_v0.cpp +++ /dev/null @@ -1,354 +0,0 @@ -/****************************************************************************** -* Copyright � 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ - -// encode decode tokens opret -// make token cryptoconditions and vouts -// This code was moved to a separate source file to enable linking libcommon.so (with importcoin.cpp which depends on some token functions) - -#include "CCtokens_v0.h" - -namespace tokensv0 { - -#ifndef IS_CHARINSTR -#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) -#endif - -// NOTE: this inital tx won't be used by other contract -// for tokens to be used there should be at least one 't' tx with other contract's custom opret -CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, vscript_t vopretNonfungible) -{ - /* CScript opret; - uint8_t evalcode = EVAL_TOKENS; - funcid = 'c'; // override the param - - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; \ - if (!vopretNonfungible.empty()) { - ss << (uint8_t)OPRETID_NONFUNGIBLEDATA; - ss << vopretNonfungible; - }); */ - - std::vector> oprets; - - if(!vopretNonfungible.empty()) - oprets.push_back(std::make_pair(OPRETID_NONFUNGIBLEDATA, vopretNonfungible)); - return EncodeTokenCreateOpRet(funcid, origpubkey, name, description, oprets); -} - -CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector> oprets) -{ - CScript opret; - uint8_t evalcode = EVAL_TOKENS; - funcid = 'c'; // override the param - - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; - for (auto o : oprets) { - if (o.first != 0) { - ss << (uint8_t)o.first; - ss << o.second; - } - }); - return(opret); -} - -/* -// opret 'i' for imported tokens -CScript EncodeTokenImportOpRet(std::vector origpubkey, std::string name, std::string description, uint256 srctokenid, std::vector> oprets) -{ - CScript opret; - uint8_t evalcode = EVAL_TOKENS; - uint8_t funcid = 'i'; - - srctokenid = revuint256(srctokenid); // do not forget this - - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description << srctokenid; - for (auto o : oprets) { - if (o.first != 0) { - ss << (uint8_t)o.first; - ss << o.second; - } - }); - return(opret); -} -*/ - - -CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::pair opretWithId) -{ - std::vector> oprets; - oprets.push_back(opretWithId); - return EncodeTokenOpRet(tokenid, voutPubkeys, oprets); -} - -CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::vector> oprets) -{ - CScript opret; - uint8_t tokenFuncId = 't'; - uint8_t evalCodeInOpret = EVAL_TOKENS; - - tokenid = revuint256(tokenid); - - uint8_t ccType = 0; - if (voutPubkeys.size() >= 0 && voutPubkeys.size() <= 2) - ccType = voutPubkeys.size(); - else { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "EncodeTokenOpRet voutPubkeys.size()=" << voutPubkeys.size() << " not supported" << std::endl); - } - - //vopret_t vpayload; - //GetOpReturnData(payload, vpayload); - - opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; - if (ccType >= 1) ss << voutPubkeys[0]; - if (ccType == 2) ss << voutPubkeys[1]; - for (auto o : oprets) { - if (o.first != 0) { - ss << (uint8_t)o.first; - ss << o.second; - } - }); - - // bad opret cases (tries to attach payload without re-serialization): - - // error "64: scriptpubkey": - // if (payload.size() > 0) - // opret += payload; - - // error "64: scriptpubkey": - // CScript opretPayloadNoOpcode(vpayload); - // return opret + opretPayloadNoOpcode; - - // error "sig_aborted": - // opret.resize(opret.size() + vpayload.size()); - // CScript::iterator it = opret.begin() + opret.size(); - // for (int i = 0; i < vpayload.size(); i++, it++) - // *it = vpayload[i]; - - return opret; -} - -// overload for compatibility -//CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector voutPubkeys, CScript payload) -//{ -// return EncodeTokenOpRet(tokenid, voutPubkeys, payload); -//} - -// overload for fungible tokens (no additional data in opret): -uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description) { - //vopret_t vopretNonfungibleDummy; - std::vector> opretsDummy; - return DecodeTokenCreateOpRet(scriptPubKey, origpubkey, name, description, opretsDummy); -} - -uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector> &oprets) -{ - vscript_t vopret, vblob; - uint8_t dummyEvalcode, funcid, opretId = 0; - - GetOpReturnData(scriptPubKey, vopret); - oprets.clear(); - - if (vopret.size() > 2 && vopret.begin()[0] == EVAL_TOKENS && vopret.begin()[1] == 'c') - { - if (E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description; - while (!ss.eof()) { - ss >> opretId; - if (!ss.eof()) { - ss >> vblob; - oprets.push_back(std::make_pair(opretId, vblob)); - } - })) - { - return(funcid); - } - } - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "DecodeTokenCreateOpRet() incorrect token create opret" << std::endl); - return (uint8_t)0; -} - -// decode token opret: -// for 't' returns all data from opret, vopretExtra contains other contract's data (currently only assets'). -// for 'c' returns only funcid. NOTE: nonfungible data is not returned -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector> &oprets) -{ - vscript_t vopret, vblob, dummyPubkey, vnonfungibleDummy; - uint8_t funcId = 0, *script, dummyEvalCode, dummyFuncId, ccType, opretId = 0; - std::string dummyName; std::string dummyDescription; - uint256 dummySrcTokenId; - CPubKey voutPubkey1, voutPubkey2; - - vscript_t voldstyledata; - bool foundOldstyle = false; - - GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - tokenid = zeroid; - oprets.clear(); - - if (script != NULL && vopret.size() > 2) - { - evalCodeTokens = script[0]; - if (evalCodeTokens != EVAL_TOKENS) { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet() incorrect evalcode in tokens opret" << std::endl); - return (uint8_t)0; - } - - funcId = script[1]; - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "DecodeTokenOpRet() decoded funcId=" << (char)(funcId ? funcId : ' ') << std::endl); - - switch (funcId) - { - case 'c': - return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, oprets); - - case 't': - - // compatibility with old-style rogue or assets data (with no opretid): - // try to unmarshal old-style rogue or assets data: - foundOldstyle = E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; - if (ccType >= 1) ss >> voutPubkey1; - if (ccType == 2) ss >> voutPubkey2; - if (!ss.eof()) { - ss >> voldstyledata; - }) && voldstyledata.size() >= 2 && - (voldstyledata.begin()[0] == 0x11 /*EVAL_ROGUE*/ && IS_CHARINSTR(voldstyledata.begin()[1], "RHQKG") || - voldstyledata.begin()[0] == EVAL_ASSETS && IS_CHARINSTR(voldstyledata.begin()[1], "sbSBxo")) ; - - if (foundOldstyle || // fix for compatibility with old style data (no opretid) - E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; - if (ccType >= 1) ss >> voutPubkey1; - if (ccType == 2) ss >> voutPubkey2; - while (!ss.eof()) { - ss >> opretId; - if (!ss.eof()) { - ss >> vblob; - oprets.push_back(std::make_pair(opretId, vblob)); - } - })) - { - if (!(ccType >= 0 && ccType <= 2)) { //incorrect ccType - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet() incorrect ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); - return (uint8_t)0; - } - - // add verification pubkeys: - voutPubkeys.clear(); - if (voutPubkey1.IsValid()) - voutPubkeys.push_back(voutPubkey1); - if (voutPubkey2.IsValid()) - voutPubkeys.push_back(voutPubkey2); - - tokenid = revuint256(tokenid); - - if (foundOldstyle) { //patch for old-style opret data with no opretid - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet() found old-style rogue/asset data, evalcode=" << (int)voldstyledata.begin()[0] << " funcid=" << (char)voldstyledata.begin()[1] << " for tokenid=" << revuint256(tokenid).GetHex() << std::endl); - uint8_t opretIdRestored; - if (voldstyledata.begin()[0] == 0x11 /*EVAL_ROGUE*/) - opretIdRestored = OPRETID_ROGUEGAMEDATA; - else // EVAL_ASSETS - opretIdRestored = OPRETID_ASSETSDATA; - - oprets.push_back(std::make_pair(opretIdRestored, voldstyledata)); - } - - return(funcId); - } - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet() bad opret format," << " ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); - return (uint8_t)0; - - default: - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet() illegal funcid=" << (int)funcId << std::endl); - return (uint8_t)0; - } - } - else { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet() empty opret, could not parse" << std::endl); - } - return (uint8_t)0; -} - - -// make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition: -CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2) -{ - // make 1of2 sigs cond - std::vector pks; - pks.push_back(CCNewSecp256k1(pk1)); - pks.push_back(CCNewSecp256k1(pk2)); - - std::vector thresholds; - thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode))); - if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()! - thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc - if (evalcode2 != 0) - thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode - thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc - - return CCNewThreshold(thresholds.size(), thresholds); -} -// overload to make two-eval (token+evalcode) 1of2 cryptocondition: -CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) { - return MakeTokensCCcond1of2(evalcode, 0, pk1, pk2); -} - -// make three-eval (token+evalcode+evalcode2) cryptocondition: -CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk) -{ - std::vector pks; - pks.push_back(CCNewSecp256k1(pk)); - - std::vector thresholds; - thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode))); - if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()! - thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc - if (evalcode2 != 0) - thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode - thresholds.push_back(CCNewThreshold(1, pks)); // signature - - return CCNewThreshold(thresholds.size(), thresholds); -} -// overload to make two-eval (token+evalcode) cryptocondition: -CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) { - return MakeTokensCCcond1(evalcode, 0, pk); -} - -// make three-eval (token+evalcode+evalcode2) 1of2 cc vout: -CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2) -{ - CTxOut vout; - CC *payoutCond = MakeTokensCCcond1of2(evalcode, evalcode2, pk1, pk2); - vout = CTxOut(nValue, CCPubKey(payoutCond)); - cc_free(payoutCond); - return(vout); -} -// overload to make two-eval (token+evalcode) 1of2 cc vout: -CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) { - return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2); -} - -// make three-eval (token+evalcode+evalcode2) cc vout: -CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk) -{ - CTxOut vout; - CC *payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk); - vout = CTxOut(nValue, CCPubKey(payoutCond)); - cc_free(payoutCond); - return(vout); -} -// overload to make two-eval (token+evalcode) cc vout: -CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) { - return MakeTokensCC1vout(evalcode, 0, nValue, pk); -} - -}; \ No newline at end of file diff --git a/src/cc/old/assets_v0.cpp b/src/cc/old/assets_v0.cpp deleted file mode 100644 index 1f6b693f2dc..00000000000 --- a/src/cc/old/assets_v0.cpp +++ /dev/null @@ -1,478 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include "CCassets_v0.h" -#include "CCtokens_v0.h" - -namespace tokensv0 { - -/* - Assets can be created or transferred. - - native coins are also locked in the EVAL_ASSETS address, so we need a strict rule on when utxo in the special address are native coins and when they are assets. The specific rule that must not be violated is that vout0 for 'b'/'B' funcid are native coins. All other utxo locked in the special address are assets. - - To create an asset use CC EVAL_ASSETS to create a transaction where vout[0] funds the assets. Externally each satoshi can be interpreted to represent 1 asset, or 100 million satoshis for one asset with 8 decimals, and the other decimals in between. The interpretation of the number of decimals is left to the higher level usages. - - Once created, the assetid is the txid of the create transaction and using the assetid/0 it can spend the assets to however many outputs it creates. The restriction is that the last output must be an opreturn with the assetid. The sum of all but the first output needs to add up to the total assetoshis input. The first output is ignored and used for change from txfee. - - What this means is that vout 0 of the creation txid and vouts 1 ... n-2 for transfer vouts are assetoshi outputs. - - There is a special type of transfer to an unspendable address, that locks the asset and creates an offer for all. The details specified in the opreturn. In order to be compatible with the signing restrictions, the "unspendable" address is actually an address whose privkey is known to all. Funds sent to this address can only be spent if the swap parameters are fulfilled, or if the original pubkey cancels the offer by spending it. - - Types of transactions: - create name:description -> txid for assetid - transfer -> [{address:amount}, ... ] // like withdraw api - selloffer -> cancel or fillsell -> mempool txid or rejected, might not confirm - buyoffer -> cancelbuy or fillbuy -> mempool txid or rejected, might not confirm - exchange -> cancel or fillexchange -> mempool txid or rejected, might not confirm - - assetsaddress // all assets end up in a special address for each pubkey - assetbalance - assetutxos - assetsbalances - asks - bids - swaps - - valid CC output: create or transfer or buyoffer or selloffer or exchange or cancel or fill - - - buyoffer: - vins.*: normal inputs (bid + change) - vout.0: amount of bid to unspendable - vout.1: CC output for marker - vout.2: normal output for change (if any) - vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey] - - cancelbuy: - vin.0: normal input - vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0] - vin.2: CC marker from buyoffer for txfee - vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey] - vout.1: vin.2 back to users pubkey - vout.2: normal output for change (if any) - vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid] 0 0 [origpubkey] - - fillbuy: - vin.0: normal input - vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0] - vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue - vout.0: remaining amount of bid to unspendable - vout.1: vin.1 value to signer of vin.2 - vout.2: vin.2 assetoshis to original pubkey - vout.3: CC output for assetoshis change (if any) - vout.4: normal output for change (if any) - vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey] - - selloffer: - vin.0: normal input - vin.1+: valid CC output for sale - vout.0: vin.1 assetoshis output to CC to unspendable - vout.1: CC output for marker - vout.2: CC output for change (if any) - vout.3: normal output for change (if any) - vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey] - - exchange: - vin.0: normal input - vin.1+: valid CC output - vout.0: vin.1 assetoshis output to CC to unspendable - vout.1: CC output for change (if any) - vout.2: normal output for change (if any) - vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey] - - cancel: - vin.0: normal input - vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx - vin.2: CC marker from selloffer for txfee - vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey] - vout.1: vin.2 back to users pubkey - vout.2: normal output for change (if any) - vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid] - - fillsell: - vin.0: normal input - vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0] - vin.2+: normal output that satisfies selloffer (*tx.vin[2])->nValue - vout.0: remaining assetoshis -> unspendable - vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any - vout.2: vin.2 value to original pubkey [origpubkey] - vout.3: CC asset for change (if any) - vout.4: CC asset2 for change (if any) 'E' only - vout.5: normal output for change (if any) - vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey] - - fillexchange: - vin.0: normal input - vin.1: unspendable.(vout.0 assetoshis from exchange) exchangeTx.vout[0] - vin.2+: valid CC assetid2 output that satisfies exchange (*tx.vin[2])->nValue - vout.0: remaining assetoshis -> unspendable - vout.1: vin.1 assetoshis to signer of vin.2 exchangeTx.vout[0].nValue -> any - vout.2: vin.2 assetoshis2 to original pubkey [origpubkey] - vout.3: normal output for change (if any) - vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey] -*/ - -using namespace tokensv0; - -// tx validation -bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransaction &tx, uint32_t nIn) -{ - static uint256 zero; - CTxDestination address; - CTransaction vinTx, createTx; - uint256 hashBlock, assetid, assetid2; - int32_t i,starti, numvins, numvouts, preventCCvins, preventCCvouts; - int64_t remaining_price, nValue, assetoshis, outputsDummy,inputs,tmpprice,totalunits,ignore; - std::vector origpubkey, tmporigpubkey, ignorepubkey, vopretNonfungible, vopretNonfungibleDummy; - uint8_t funcid, evalCodeInOpret; - char destaddr[64], origNormalAddr[64], origTokensCCaddr[64], origCCaddrDummy[64]; - char tokensDualEvalUnspendableCCaddr[64], origAssetsCCaddr[64]; - - //return true; - - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - outputsDummy = inputs = 0; - preventCCvins = preventCCvouts = -1; - - // add specific chains exceptions for old token support: - if (strcmp(ASSETCHAINS_SYMBOL, "SEC") == 0 && chainActive.Height() <= 144073) - return true; - - if (strcmp(ASSETCHAINS_SYMBOL, "MGNX") == 0 && chainActive.Height() <= 210190) - return true; - - // add specific chains exceptions for old token support: - if (strcmp(ASSETCHAINS_SYMBOL, "SEC") == 0 && chainActive.Height() <= 144073) - return true; - - if (strcmp(ASSETCHAINS_SYMBOL, "MGNX") == 0 && chainActive.Height() <= 210190) - return true; - - if (numvouts == 0) - return eval->Invalid("AssetValidate: no vouts"); - - if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 ) - return eval->Invalid("AssetValidate: invalid opreturn payload"); - - // non-fungible tokens support: - GetNonfungibleData(assetid, vopretNonfungible); - if (vopretNonfungible.size() > 0) - cpAssets->evalcodeNFT = vopretNonfungible.begin()[0]; - - // find dual-eval tokens unspendable addr: - GetTokensCCaddress(cpAssets, tokensDualEvalUnspendableCCaddr, GetUnspendable(cpAssets, NULL)); - // this is for marker validation: - GetCCaddress(cpAssets, origAssetsCCaddr, origpubkey); - - // we need this for validating single-eval tokens' vins/vous: - struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - - // find single-eval token user cc addr: - //GetCCaddress(cpTokens, signleEvalTokensCCaddr, pubkey2pk(origpubkey)); - - //fprintf(stderr,"AssetValidate (%c)\n",funcid); - - if( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid, createTx, hashBlock) == 0 ) - return eval->Invalid("cant find asset create txid"); - else if( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2, createTx, hashBlock) == 0 ) - return eval->Invalid("cant find asset2 create txid"); - else if( IsCCInput(tx.vin[0].scriptSig) != 0 ) // vin0 should be normal vin - return eval->Invalid("illegal asset vin0"); - else if( numvouts < 2 ) - return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below - else if( funcid != 'c' ) - { - /* if( funcid == 't' ) - starti = 0; - else - starti = 1; */ - - if( assetid == zero ) - return eval->Invalid("illegal assetid"); - - else if (!AssetCalcAmounts(cpAssets, inputs, outputsDummy/*outputsDummy is calculated incorrectly but not used*/, eval, tx, assetid)) { // Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs - return false; // returns false if some problems with reading vintxes - } - } - - switch( funcid ) - { - case 'c': // create wont be called to be verified as it has no CC inputs - //vin.0: normal input - //vout.0: issuance assetoshis to CC - //vout.1: normal output for change (if any) - //vout.n-1: opreturn [EVAL_ASSETS] ['c'] [{"":""}] - //if (evalCodeInOpret == EVAL_ASSETS) - // return eval->Invalid("unexpected AssetValidate for createasset"); - // return - return eval->Invalid("invalid asset funcid \'c\'"); - break; - case 't': // transfer - //vin.0: normal input - //vin.1 .. vin.n-1: valid CC outputs - //vout.0 to n-2: assetoshis output to CC - //vout.n-2: normal output for change (if any) - //vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid] - //if (inputs == 0) - // return eval->Invalid("no asset inputs for transfer"); - //fprintf(stderr,"transfer preliminarily validated %.8f -> %.8f (%d %d)\n",(double)inputs/COIN,(double)outputs/COIN,preventCCvins,preventCCvouts); - return eval->Invalid("invalid asset funcid \'t\'"); - break; - - case 'b': // buyoffer - //vins.*: normal inputs (bid + change) - //vout.0: amount of bid to unspendable - //vout.1: CC output for marker - //vout.2: normal output for change (if any) - // vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey] - - // as we don't use tokenconvert we should not be here: - return eval->Invalid("invalid asset funcid (b)"); - - if( remaining_price == 0 ) - return eval->Invalid("illegal null amount for buyoffer"); - else if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr,0) == 0 ) // coins to assets unspendable cc addr - return eval->Invalid("invalid vout for buyoffer"); - preventCCvins = 1; - preventCCvouts = 1; - fprintf(stderr,"buy offer validated to destaddr.(%s)\n",cpAssets->unspendableCCaddr); - break; - - case 'o': // cancelbuy - //vin.0: normal input - //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0] - //vin.2: CC marker from buyoffer for txfee - //vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey] - //vout.1: vin.2 back to users pubkey - //vout.2: normal output for change (if any) - //vout.n-1: opreturn [EVAL_ASSETS] ['o'] - if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, origCCaddrDummy, origNormalAddr, tx, assetid)) == 0 ) - return(false); - else if( ConstrainVout(tx.vout[0],0, origNormalAddr, nValue) == 0 ) - return eval->Invalid("invalid refund for cancelbuy"); - preventCCvins = 3; - preventCCvouts = 0; - //fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origNormalAddr); - break; - - case 'B': // fillbuy: - //vin.0: normal input - //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0] - //vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue - //vout.0: remaining amount of bid to unspendable - //vout.1: vin.1 value to signer of vin.2 - //vout.2: vin.2 assetoshis to original pubkey - //vout.3: CC output for assetoshis change (if any) - //vout.4: normal output for change (if any) - //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey] - preventCCvouts = 4; - - if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) - return(false); - else if( numvouts < 4 ) - return eval->Invalid("not enough vouts for fillbuy"); - else if( tmporigpubkey != origpubkey ) - return eval->Invalid("mismatched origpubkeys for fillbuy"); - else - { - if( nValue != tx.vout[0].nValue + tx.vout[1].nValue ) - return eval->Invalid("locked value doesnt match vout0+1 fillbuy"); - else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change present - { - if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals) - return eval->Invalid("vout2 doesnt go to origpubkey fillbuy"); - else if ( inputs != tx.vout[2].nValue + tx.vout[4].nValue ) - return eval->Invalid("asset inputs doesnt match vout2+3 fillbuy"); - preventCCvouts ++; - } - else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals) - return eval->Invalid("vout2 doesnt match inputs fillbuy"); - else if( ConstrainVout(tx.vout[1], 0, NULL, 0) == 0 ) - return eval->Invalid("vout1 is CC for fillbuy"); - else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to asset cc addr - return eval->Invalid("invalid marker for original pubkey"); - else if( ValidateBidRemainder(remaining_price, tx.vout[0].nValue, nValue, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) - return eval->Invalid("mismatched remainder for fillbuy"); - else if( remaining_price != 0 ) - { - if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins to asset unspendable cc addr - return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy"); - } - } - //fprintf(stderr,"fillbuy validated\n"); - break; - //case 'e': // selloffer - // break; // disable swaps - case 's': // selloffer - //vin.0: normal input - //vin.1+: valid CC output for sale - //vout.0: vin.1 assetoshis output to CC to unspendable - //vout.1: CC output for marker - //vout.2: CC output for change (if any) - //vout.3: normal output for change (if any) - //'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey] - //'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey] - - // as we don't use tokenconvert we should not be here: - return eval->Invalid("invalid asset funcid (s)"); - - preventCCvouts = 2; - if( remaining_price == 0 ) - return eval->Invalid("illegal null remaining_price for selloffer"); - if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("invalid normal vout1 for sellvin"); - if( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change presents - { - preventCCvouts++; - if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, 0) == 0 ) // tokens to tokens unspendable cc addr. TODO: this in incorrect, should be assets if we got here! - return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); - else if( tx.vout[0].nValue + tx.vout[2].nValue != inputs ) - return eval->Invalid("mismatched vout0+vout2 total for selloffer"); - } - // no cc change: - else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // tokens to tokens unspendable cc addr TODO: this in incorrect, should be assets if got here! - return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); - //fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price); - break; - - case 'x': // cancel sell - //vin.0: normal input - //vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx - //vin.2: CC marker from selloffer for txfee - //vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey] - //vout.1: vin.2 back to users pubkey - //vout.2: normal output for change (if any) - //vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid] - - if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) // NOTE: - return(false); - else if( ConstrainVout(tx.vout[0], 1, origTokensCCaddr, assetoshis) == 0 ) // tokens returning to originator cc addr - return eval->Invalid("invalid vout for cancel"); - preventCCvins = 3; - preventCCvouts = 1; - break; - - case 'S': // fillsell - //vin.0: normal input - //vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0] - //'S'.vin.2+: normal output that satisfies selloffer (*tx.vin[2])->nValue - //vout.0: remaining assetoshis -> unspendable - //vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any - //'S'.vout.2: vin.2 value to original pubkey [origpubkey] - //vout.3: normal output for change (if any) - //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey] - - if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) - return(false); - else if( numvouts < 4 ) - return eval->Invalid("not enough vouts for fillask"); - else if( tmporigpubkey != origpubkey ) - return eval->Invalid("mismatched origpubkeys for fillask"); - else - { - if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue ) - return eval->Invalid("locked value doesnt match vout0+1 fillask"); - if( ValidateAskRemainder(remaining_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) - return eval->Invalid("mismatched remainder for fillask"); - else if( ConstrainVout(tx.vout[1], 1, NULL, 0) == 0 ) // do not check token buyer's cc addr - return eval->Invalid("normal vout1 for fillask"); - else if( ConstrainVout(tx.vout[2], 0, origNormalAddr, 0) == 0 ) // coins to originator normal addr - return eval->Invalid("normal vout1 for fillask"); - else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to originator asset cc addr - return eval->Invalid("invalid marker for original pubkey"); - else if( remaining_price != 0 ) - { - if ( ConstrainVout(tx.vout[0], 1, tokensDualEvalUnspendableCCaddr, 0) == 0 ) - return eval->Invalid("mismatched vout0 assets dual unspendable CCaddr for fill sell"); - } - } - //fprintf(stderr,"fill validated\n"); - break; - case 'E': // fillexchange - ////////// not implemented yet //////////// - return eval->Invalid("unexpected assets fillexchange funcid"); - break; // disable asset swaps - //vin.0: normal input - //vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0] - //vin.2+: valid CC assetid2 output that satisfies exchange (*tx.vin[2])->nValue - //vout.0: remaining assetoshis -> unspendable - //vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any - //vout.2: vin.2+ assetoshis2 to original pubkey [origpubkey] - //vout.3: CC output for asset2 change (if any) - //vout.3/4: normal output for change (if any) - //vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey] - - //if ( AssetExactAmounts(false, cp,inputs,outputs,eval,tx,assetid2) == false ) - // eval->Invalid("asset2 inputs != outputs"); - - ////////// not implemented yet //////////// - if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) - return(false); - else if( numvouts < 3 ) - return eval->Invalid("not enough vouts for fillex"); - else if( tmporigpubkey != origpubkey ) - return eval->Invalid("mismatched origpubkeys for fillex"); - else - { - if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue ) - return eval->Invalid("locked value doesnt match vout0+1 fillex"); - else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 ) - ////////// not implemented yet //////////// - { - if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 ) - return eval->Invalid("vout2 doesnt go to origpubkey fillex"); - else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue ) - { - fprintf(stderr,"inputs %.8f != %.8f + %.8f\n",(double)inputs/COIN,(double)tx.vout[2].nValue/COIN,(double)tx.vout[3].nValue/COIN); - return eval->Invalid("asset inputs doesnt match vout2+3 fillex"); - } - } - ////////// not implemented yet //////////// - else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 ) - return eval->Invalid("vout2 doesnt match inputs fillex"); - else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 ) - return eval->Invalid("vout1 is CC for fillex"); - fprintf(stderr,"assets vout0 %llu, vin1 %llu, vout2 %llu -> orig, vout1 %llu, total %llu\n",(long long)tx.vout[0].nValue,(long long)assetoshis,(long long)tx.vout[2].nValue,(long long)tx.vout[1].nValue,(long long)totalunits); - if( ValidateSwapRemainder(remaining_price, tx.vout[0].nValue, assetoshis,tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) - return eval->Invalid("mismatched remainder for fillex"); - else if( ConstrainVout(tx.vout[1], 1, 0, 0) == 0 ) - ////////// not implemented yet //////////// - return eval->Invalid("normal vout1 for fillex"); - else if( remaining_price != 0 ) - { - if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == 0 ) // TODO: unsure about this, but this is not impl yet anyway - return eval->Invalid("mismatched vout0 AssetsCCaddr for fillex"); - } - } - ////////// not implemented yet //////////// - //fprintf(stderr,"fill validated\n"); - break; - - default: - fprintf(stderr,"illegal assets funcid.(%c)\n",funcid); - return eval->Invalid("unexpected assets funcid"); - //break; - } - - // what does this do? - bool bPrevent = PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts); // seems we do not need this call as we already checked vouts well - //std::cerr << "AssetsValidate() PreventCC returned=" << bPrevent << std::endl; - return (bPrevent); -} - -}; diff --git a/src/cc/old/heir_v0.cpp b/src/cc/old/heir_v0.cpp deleted file mode 100644 index 6429c1a25b1..00000000000 --- a/src/cc/old/heir_v0.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -// #include "CCHeir_v0.h" -#include "heir_validate_v0.h" -#include - -namespace heirv0 { - -// makes coin initial tx opret -vscript_t EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) -{ - uint8_t evalcode = EVAL_HEIR; - - return E_MARSHAL(ss << evalcode << funcid << ownerPubkey << heirPubkey << inactivityTimeSec << heirName << memo); -} - -// makes coin additional tx opret -vscript_t EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t hasHeirSpendingBegun) -{ - uint8_t evalcode = EVAL_HEIR; - - fundingtxid = revuint256(fundingtxid); - return E_MARSHAL(ss << evalcode << funcid << fundingtxid << hasHeirSpendingBegun); -} - - -// decode opret vout for Heir contract -uint8_t _DecodeHeirOpRet(vscript_t vopret, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) -{ - uint8_t evalCodeInOpret = 0; - uint8_t heirFuncId = 0; - - fundingTxidInOpret = zeroid; //to init - - evalCodeInOpret = vopret.begin()[0]; - - if (vopret.size() > 1 && evalCodeInOpret == EVAL_HEIR) { - // NOTE: it unmarshals for all F, A and C - uint8_t heirFuncId = 0; - hasHeirSpendingBegun = 0; - - bool result = E_UNMARSHAL(vopret, { ss >> evalCodeInOpret; ss >> heirFuncId; - if (heirFuncId == 'F') { - ss >> ownerPubkey; ss >> heirPubkey; ss >> inactivityTime; ss >> heirName; ss >> memo; - } - else { - ss >> fundingTxidInOpret >> hasHeirSpendingBegun; - } - }); - - if (!result) { - // if (!noLogging) std::cerr << "_DecodeHeirOpRet() could not unmarshal opret, evalCode=" << (int)evalCodeInOpret << std::endl; - return (uint8_t)0; - } - - if (isMyFuncId(heirFuncId)) { - fundingTxidInOpret = revuint256(fundingTxidInOpret); - return heirFuncId; - } - else { - if(!noLogging) std::cerr << "_DecodeHeirOpRet() unexpected opret type, heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') << std::endl; - } - } - else { - if (!noLogging) std::cerr << "_DecodeHeirOpRet() not a heir opret, vopretExtra.size() == 0 or not EVAL_HEIR evalcode=" << (int)evalCodeInOpret << std::endl; - } - return (uint8_t)0; -} - -// decode combined opret: -uint8_t _DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) -{ - uint8_t evalCodeTokens = 0; - std::vector voutPubkeysDummy; - std::vector oprets; - vscript_t vopretExtra /*, vopretStripped*/; - - - if (DecodeTokenOpRetV1(scriptPubKey, tokenid, voutPubkeysDummy, oprets) != 0 && GetOpReturnCCBlob(oprets, vopretExtra)) { - if (vopretExtra.size() < 1) { - if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() empty vopretExtra" << std::endl; - return (uint8_t)0; - } - } - else { - GetOpReturnData(scriptPubKey, vopretExtra); - } - - return _DecodeHeirOpRet(vopretExtra, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); -} - -// overload to decode opret in fundingtxid: -uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging) { - uint256 dummyFundingTxidInOpret; - uint8_t dummyHasHeirSpendingBegun; - - return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, dummyFundingTxidInOpret, dummyHasHeirSpendingBegun, noLogging); -} - -// overload to decode opret in A and C heir tx: -uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { - CPubKey dummyOwnerPubkey, dummyHeirPubkey; - int64_t dummyInactivityTime; - std::string dummyHeirName, dummyMemo; - - return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, dummyMemo, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); -} - -// check if pubkey is in vins -void CheckVinPubkey(std::vector vins, CPubKey pubkey, bool &hasPubkey, bool &hasOtherPubkey) { - - hasPubkey = false; - hasOtherPubkey = false; - - for (auto vin : vins) { - CPubKey vinPubkey = check_signing_pubkey(vin.scriptSig); - if (vinPubkey.IsValid()) { - if (vinPubkey == pubkey) - hasPubkey = true; - if (vinPubkey != pubkey) - hasOtherPubkey = true; - } - } -} - -/** - * find the latest funding tx: it may be the first F tx or one of A or C tx's - * Note: this function is also called from validation code (use non-locking calls) - */ -uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, CScript& fundingOpretScript, uint8_t &hasHeirSpendingBegun) -{ - CTransaction fundingtx; - uint256 hashBlock; - const bool allowSlow = false; - - hasHeirSpendingBegun = 0; - funcId = 0; - - // get initial funding tx and set it as initial lasttx: - if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { - - CScript heirScript = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); - uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); - if (funcId != 0) { - // found at least funding tx! - //std::cerr << "FindLatestFundingTx() lasttx currently is fundingtx, txid=" << fundingtxid.GetHex() << " opreturn type=" << (char)funcId << '\n'; - fundingOpretScript = fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey; - } else { - std::cerr << "FindLatestFundingTx() could not decode opreturn for fundingtxid=" << fundingtxid.GetHex() << '\n'; - return zeroid; - } - } else { - std::cerr << "FindLatestFundingTx() could not find funding tx for fundingtxid=" << fundingtxid.GetHex() << '\n'; - return zeroid; - } - - // TODO: correct cc addr: - std::vector> unspentOutputs; - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_HEIR); - char coinaddr[64]; - GetCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' - - SetCCunspents(unspentOutputs, coinaddr,true); // get vector with tx's with unspent vouts of 1of2pubkey address: - //std::cerr << "FindLatestFundingTx() using 1of2address=" << coinaddr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; - - int32_t maxBlockHeight = 0; // max block height - uint256 latesttxid = fundingtxid; - - // try to find the last funding or spending tx by checking fundingtxid in 'opreturn': - for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { - CTransaction regtx; - uint256 hash; - - uint256 txid = it->first.txhash; - //std::cerr << "FindLatestFundingTx() checking unspents for txid=" << txid.GetHex() << '\n'; - - int32_t blockHeight = (int32_t)it->second.blockHeight; - - //NOTE: maybe called from validation code: - if (myGetTransaction(txid, regtx, hash)) { - //std::cerr << "FindLatestFundingTx() found tx for txid=" << txid.GetHex() << " blockHeight=" << blockHeight << " maxBlockHeight=" << maxBlockHeight << '\n'; - uint256 fundingTxidInOpret; - uint256 tokenidInOpret; // not to contaminate the tokenid from the params! - uint8_t tmpFuncId; - uint8_t hasHeirSpendingBegunInOpret; - - CScript heirScript = (regtx.vout.size() > 0) ? regtx.vout[regtx.vout.size() - 1].scriptPubKey : CScript(); - tmpFuncId = DecodeHeirEitherOpRet(heirScript, tokenidInOpret, fundingTxidInOpret, hasHeirSpendingBegunInOpret, true); - if (tmpFuncId != 0 && fundingtxid == fundingTxidInOpret && (tokenid == zeroid || tokenid == tokenidInOpret)) { // check tokenid also - - if (blockHeight > maxBlockHeight) { - - // check owner pubkey in vins - bool isOwner = false; - bool isNonOwner = false; - - CheckVinPubkey(regtx.vin, ownerPubkey, isOwner, isNonOwner); - - // we ignore 'donations' tx (with non-owner inputs) for calculating if heir is allowed to spend: - if (isOwner && !isNonOwner) { - hasHeirSpendingBegun = hasHeirSpendingBegunInOpret; - maxBlockHeight = blockHeight; - latesttxid = txid; - funcId = tmpFuncId; - } - - //std::cerr << "FindLatestFundingTx() txid=" << latesttxid.GetHex() << " at blockHeight=" << maxBlockHeight - // << " opreturn type=" << (char)(funcId ? funcId : ' ') << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << " - set as current lasttxid" << '\n'; - } - } - } - } - - return latesttxid; -} - -}; \ No newline at end of file diff --git a/src/cc/old/heir_validate_v0.h b/src/cc/old/heir_validate_v0.h deleted file mode 100644 index 45939889f76..00000000000 --- a/src/cc/old/heir_validate_v0.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef HEIR_VALIDATE_V0_H -#define HEIR_VALIDATE_V0_H - -#include "../CCinclude.h" -//#include "CCHeir_v0.h" - -namespace heirv0 { - -#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) - -// makes coin initial tx opret -vscript_t EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo); -vscript_t EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t isHeirSpendingBegan); - -uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &isHeirSpendingBegan); -uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, CScript& fundingOpretScript, uint8_t &hasHeirSpendingBegun); - -uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging = false); -uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging = false); -uint8_t _DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging); - -inline static bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); } -inline static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); } - -}; - -#endif diff --git a/src/cc/oracles.cpp b/src/cc/oracles.cpp index de29021e64e..61e5c961959 100644 --- a/src/cc/oracles.cpp +++ b/src/cc/oracles.cpp @@ -95,7 +95,6 @@ extern int32_t komodo_get_current_height(); #define PUBKEY_SPOOFING_FIX_ACTIVATION 1563148800 #define CC_MARKER_VALUE 10000 -#define CC_TXFEE 10000 // start of consensus code CScript EncodeOraclesCreateOpRet(uint8_t funcid,std::string name,std::string description,std::string format) @@ -180,8 +179,8 @@ CPubKey OracleBatonPk(char *batonaddr,struct CCcontract_info *cp) cp->unspendablepriv2[i] = (priv[i] ^ cp->CCpriv[i]); while ( secp256k1_ec_seckey_verify(ctx,cp->unspendablepriv2) == 0 ) { - // for (i=0; i<32; i++) - // fprintf(stderr,"%02x",cp->unspendablepriv2[i]); + for (i=0; i<32; i++) + fprintf(stderr,"%02x",cp->unspendablepriv2[i]); fprintf(stderr," invalid privkey\n"); if ( secp256k1_ec_privkey_tweak_add(ctx,cp->unspendablepriv2,priv) != 0 ) break; @@ -201,16 +200,14 @@ CPubKey OracleBatonPk(char *batonaddr,struct CCcontract_info *cp) int64_t OracleCurrentDatafee(uint256 reforacletxid,char *markeraddr,CPubKey publisher) { uint256 txid,oracletxid,hashBlock; int64_t datafee=0,dfee; int32_t dheight=0,vout,height,numvouts; CTransaction tx; CPubKey pk; - std::vector > unspentOutputs; struct CCcontract_info *cp,C; - - cp = CCinit(&C,EVAL_ORACLES); + std::vector > unspentOutputs; SetCCunspents(unspentOutputs,markeraddr,false); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; height = (int32_t)it->second.blockHeight; - if ( (GetLatestTimestamp(komodo_currentheight()) 0 ) + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) { if ( DecodeOraclesOpRet(tx.vout[numvouts-1].scriptPubKey,oracletxid,pk,dfee) == 'R' ) { @@ -251,7 +248,6 @@ int64_t OracleDatafee(CScript &scriptPubKey,uint256 oracletxid,CPubKey publisher static uint256 myIs_baton_spentinmempool(uint256 batontxid,int32_t batonvout) { std::vector tmp_txs; - myGet_mempool_txs(tmp_txs,EVAL_ORACLES,'D'); for (std::vector::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++) { @@ -266,24 +262,22 @@ static uint256 myIs_baton_spentinmempool(uint256 batontxid,int32_t batonvout) return(batontxid); } -uint256 OracleBatonUtxo(uint64_t value,struct CCcontract_info *cp,uint256 reforacletxid,char *batonaddr,CPubKey publisher,std::vector &dataarg) +uint256 OracleBatonUtxo(uint64_t txfee,struct CCcontract_info *cp,uint256 reforacletxid,char *batonaddr,CPubKey publisher,std::vector &dataarg) { - uint256 txid,oracletxid,hashBlock,btxid,batontxid = zeroid; int64_t dfee; int32_t dheight=0,vout,height,numvouts; - CTransaction tx; CPubKey pk; uint8_t *ptr; std::vector vopret,data; + uint256 txid,oracletxid,hashBlock,btxid,batontxid = zeroid; int64_t dfee; int32_t dheight=0,vout,height,numvouts; CTransaction tx; CPubKey pk; uint8_t *ptr; std::vector vopret,data; std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,batonaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; height = (int32_t)it->second.blockHeight; - if ( it->second.satoshis != value ) + if ( it->second.satoshis != txfee ) { - //fprintf(stderr,"it->second.satoshis %llu != %llu txfee\n",(long long)it->second.satoshis,(long long)value); + fprintf(stderr,"it->second.satoshis %llu != %llu txfee\n",(long long)it->second.satoshis,(long long)txfee); continue; } - if ( FetchCCtx(txid,tx,cp) && (numvouts= tx.vout.size()) > 0 ) + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) { GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); if ( vopret.size() > 2 ) @@ -325,12 +319,12 @@ uint256 OraclesBatontxid(uint256 reforacletxid,CPubKey refpk) txid = it->first.txhash; //fprintf(stderr,"check %s\n",uint256_str(str,txid)); height = (int32_t)it->second.blockHeight; - if ( FetchCCtx(txid,regtx,cp) ) + if ( myGetTransaction(txid,regtx,hash) != 0 ) { if ( regtx.vout.size() > 0 && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R' && oracletxid == reforacletxid && pk == refpk ) { Getscriptaddress(batonaddr,regtx.vout[1].scriptPubKey); - batontxid = OracleBatonUtxo(CC_MARKER_VALUE,cp,oracletxid,batonaddr,pk,data); + batontxid = OracleBatonUtxo(10000,cp,oracletxid,batonaddr,pk,data); break; } } @@ -554,7 +548,7 @@ int32_t oracleprice_add(std::vector &publishers,CPubKey if ( regtx.vout.size() > 0 && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R' && oracletxid == reforacletxid ) { Getscriptaddress(batonaddr,regtx.vout[1].scriptPubKey); - batontxid = OracleBatonUtxo(cp,oracletxid,batonaddr,pk,data); + batontxid = OracleBatonUtxo(10000,cp,oracletxid,batonaddr,pk,data); if ( batontxid != zeroid && (ht= oracleprice_add(publishers,pk,ht,data,maxheight)) > maxheight ) maxheight = ht; } @@ -653,7 +647,7 @@ bool OraclesDataValidate(struct CCcontract_info *cp,Eval* eval,const CTransactio bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - uint256 oracletxid,batontxid,txid; int32_t numvins,numvouts,preventCCvins,preventCCvouts; int64_t amount; uint256 hashblock; + uint256 oracletxid,batontxid,txid; uint64_t txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts; int64_t amount; uint256 hashblock; uint8_t *script; std::vector vopret,data; CPubKey publisher,tmppk,oraclespk; char tmpaddress[64],vinaddress[64],oraclesaddr[64]; CTransaction tmptx; std::string name,desc,format; @@ -664,11 +658,6 @@ bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t return eval->Invalid("no vouts"); else { - if (GetLatestTimestamp(komodo_currentheight())>=MAY2020_NNELECTION_HARDFORK) - { - CCOpretCheck(eval,tx,true,true,true); - ExactAmounts(eval,tx,CC_TXFEE); - } GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); if ( vopret.size() > 2 ) { @@ -679,22 +668,22 @@ bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t { case 'C': // create // vins.*: normal inputs - // vout.0: marker to oracle normal address + // vout.0: txfee tag to oracle normal address // vout.1: change, if any // vout.n-1: opreturn with name and description and format for data return eval->Invalid("unexpected OraclesValidate for create"); break; case 'F': // fund (activation on Jul 15th 2019 00:00) // vins.*: normal inputs - // vout.0: CC marker fee to oracle CC address of users pubkey + // vout.0: txfee to oracle CC address of users pubkey // vout.1: change, if any // vout.n-1: opreturn with createtxid, pubkey and amount return eval->Invalid("unexpected OraclesValidate for create"); break; case 'R': // register // vin.0: normal inputs - // vin.1: CC input from pubkeys oracle CC addres - to prove that register came from pubkey that is registred (activation on Jul 15th 2019 00:00) - // vout.0: marker to oracle normal address + // vin.n-1: CC input from pubkeys oracle CC addres - to prove that register came from pubkey that is registred (activation on Jul 15th 2019 00:00) + // vout.0: txfee tag to normal marker address // vout.1: baton CC utxo // vout.2: marker from oraclesfund tx to normal pubkey address (activation on Jul 15th 2019 00:00) // vout.n-2: change, if any @@ -709,14 +698,21 @@ bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t return eval->Invalid("invalid oraclescreate OP_RETURN data!"); else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) return eval->Invalid("vin.0 is normal for oraclescreate!"); - else if (ConstrainVout(tmptx.vout[0],0,oraclesaddr,CC_MARKER_VALUE)==0) + else if (ConstrainVout(tmptx.vout[0],0,oraclesaddr,txfee)==0) return eval->Invalid("invalid marker for oraclescreate!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) return eval->Invalid("vin.0 is normal for oraclesregister!"); - else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || DecodeOraclesOpRet(tmptx.vout[tmptx.vout.size()-1].scriptPubKey,txid,tmppk,amount)!='F' - || !GetCCaddress(cp,tmpaddress,tmppk) || ConstrainVout(tmptx.vout[tx.vin[1].prevout.n],1,tmpaddress,CC_MARKER_VALUE)==0 || oracletxid!=txid) - return eval->Invalid("invalid vin.1 for oraclesregister, it must be CC vin or pubkey not same as vin pubkey, register and fund tx must be done from owner of pubkey that registers to oracle!!"); - else if (CCtxidaddr(tmpaddress,oracletxid).IsValid() && ConstrainVout(tx.vout[0],0,tmpaddress,CC_MARKER_VALUE)==0) + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 && (*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0) + return eval->Invalid("there is no CC vin from oraclesfund tx"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 1 && (myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || DecodeOraclesOpRet(tmptx.vout[tmptx.vout.size()-1].scriptPubKey,txid,tmppk,amount)!='F' + || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE || !Getscriptaddress(vinaddress,tmptx.vout[tx.vin[1].prevout.n].scriptPubKey) + || !GetCCaddress(cp,tmpaddress,tmppk) || strcmp(tmpaddress,vinaddress)!=0) || oracletxid!=txid) + return eval->Invalid("invalid vin.1 for oraclesregister, it must be CC vin or pubkey not same as vin pubkey, register and fund tx must be done from owner of pubkey that registers to oracle!!"); + else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 1 && (myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || DecodeOraclesOpRet(tmptx.vout[tmptx.vout.size()-1].scriptPubKey,txid,tmppk,amount)!='F' + || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE || !Getscriptaddress(vinaddress,tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].scriptPubKey) + || !GetCCaddress(cp,tmpaddress,tmppk) || strcmp(tmpaddress,vinaddress)!=0) || oracletxid!=txid) + return eval->Invalid("invalid vin."+std::to_string(tx.vin.size()-1)+" for oraclesregister, it must be CC vin or pubkey not same as vin pubkey, register and fund tx must be done from owner of pubkey that registers to oracle!!"); + else if (CCtxidaddr(tmpaddress,oracletxid).IsValid() && ConstrainVout(tx.vout[0],0,tmpaddress,txfee)==0) return eval->Invalid("invalid marker for oraclesregister!"); else if (!Getscriptaddress(tmpaddress,CScript() << ParseHex(HexStr(tmppk)) << OP_CHECKSIG) || ConstrainVout(tx.vout[2],0,tmpaddress,CC_MARKER_VALUE)==0) return eval->Invalid("pubkey in OP_RETURN and in vout.2 not matching, register must be done from owner of pubkey that registers to oracle!"); @@ -763,10 +759,8 @@ bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t int64_t AddOracleInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,uint256 oracletxid,CPubKey pk,int64_t total,int32_t maxinputs) { - char coinaddr[64],funcid; int64_t nValue,price,totalinputs = 0; uint256 tmporacletxid,tmpbatontxid,txid,hashBlock; - std::vector origpubkey,data; CTransaction vintx; int32_t numvouts,vout,n = 0; + char coinaddr[64],funcid; int64_t nValue,price,totalinputs = 0; uint256 tmporacletxid,tmpbatontxid,txid,hashBlock; std::vector origpubkey,data; CTransaction vintx; int32_t numvouts,vout,n = 0; std::vector > unspentOutputs; CPubKey tmppk; int64_t tmpnum; - GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr,true); //fprintf(stderr,"addoracleinputs from (%s)\n",coinaddr); @@ -779,12 +773,12 @@ int64_t AddOracleInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,uint { if ((funcid=DecodeOraclesOpRet(vintx.vout[numvouts].scriptPubKey,tmporacletxid,tmppk,tmpnum))!=0 && (funcid=='S' || funcid=='D')) { - if (funcid=='D' && ValidateCCtx(vintx,cp) && DecodeOraclesData(vintx.vout[numvouts].scriptPubKey,tmporacletxid,tmpbatontxid,tmppk,data)==0) + if (funcid=='D' && DecodeOraclesData(vintx.vout[numvouts].scriptPubKey,tmporacletxid,tmpbatontxid,tmppk,data)==0) fprintf(stderr,"invalid oraclesdata transaction \n"); else if (tmporacletxid==oracletxid) { // get valid CC payments - if ( (nValue= IsOraclesvout(cp,vintx,vout)) >= CC_MARKER_VALUE && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + if ( (nValue= IsOraclesvout(cp,vintx,vout)) >= 10000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); @@ -806,7 +800,7 @@ int64_t LifetimeOraclesFunds(struct CCcontract_info *cp,uint256 oracletxid,CPubK char coinaddr[64]; CPubKey pk; int64_t total=0,num; uint256 txid,hashBlock,subtxid; CTransaction subtx; std::vector txids; GetCCaddress(cp,coinaddr,publisher); - SetCCtxids(txids,coinaddr,true,cp->evalcode,0,oracletxid,'S'); + SetCCtxids(txids,coinaddr,true,cp->evalcode,oracletxid,'S'); //fprintf(stderr,"scan lifetime of %s\n",coinaddr); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { @@ -890,12 +884,12 @@ UniValue OracleCreate(const CPubKey& pk, int64_t txfee,std::string name,std::str } } if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_ORACLES]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); Oraclespk = GetUnspendable(cp,0); - if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0 ) + if ( AddNormalinputs(mtx,mypk,2*txfee,3,pk.IsValid()) > 0 ) { - mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(Oraclespk)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(Oraclespk)) << OP_CHECKSIG)); return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeOraclesCreateOpRet('C',name,description,format))); } CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "error adding normal inputs"); @@ -909,12 +903,12 @@ UniValue OracleFund(const CPubKey& pk, int64_t txfee,uint256 oracletxid) if (GetLatestTimestamp(komodo_currentheight())PUBKEY_SPOOFING_FIX_ACTIVATION && AddMyOraclesFunds(cp,mtx,mypk,oracletxid)!=CC_MARKER_VALUE) CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "error adding inputs from your Oracles CC address, please fund it first with oraclesfund rpc!"); - mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,batonpk)); - if (GetLatestTimestamp(komodo_get_current_height())>PUBKEY_SPOOFING_FIX_ACTIVATION) mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,batonpk)); + if (GetLatestTimestamp(komodo_get_current_height())>PUBKEY_SPOOFING_FIX_ACTIVATION) mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeOraclesOpRet('R',oracletxid,mypk,datafee))); } CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "error adding normal inputs"); @@ -961,17 +955,17 @@ UniValue OracleSubscribe(const CPubKey& pk, int64_t txfee,uint256 oracletxid,CPu CPubKey mypk,markerpubkey; struct CCcontract_info *cp,C; char markeraddr[64]; std::string name,desc,format; int32_t numvouts; uint256 hashBlock; cp = CCinit(&C,EVAL_ORACLES); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_ORACLES]?0:CC_TXFEE; + txfee = 10000; if (myGetTransaction(oracletxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); + CCERR_RESULT("oraclecc",CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); if (DecodeOraclesCreateOpRet(tx.vout[numvouts-1].scriptPubKey,name,desc,format)!='C') - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); + CCERR_RESULT("oraclecc",CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); markerpubkey = CCtxidaddr(markeraddr,oracletxid); - if ( AddNormalinputs(mtx,mypk,amount + txfee + CC_MARKER_VALUE,64,pk.IsValid()) > 0 ) + if ( AddNormalinputs(mtx,mypk,amount + 2*txfee,64,pk.IsValid()) > 0 ) { mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,publisher)); - mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeOraclesOpRet('S',oracletxid,mypk,amount))); } CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "error adding normal inputs"); @@ -1002,16 +996,16 @@ UniValue OracleData(const CPubKey& pk, int64_t txfee,uint256 oracletxid,std::vec else CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "invalid oracle txid"); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_ORACLES]?0:CC_TXFEE; + txfee = 10000; GetCCaddress(cp,coinaddr,mypk); - if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0 ) // have enough funds even if baton utxo not there + if ( AddNormalinputs(mtx,mypk,2*txfee,3,pk.IsValid()) > 0 ) // have enough funds even if baton utxo not there { batonpk = OracleBatonPk(batonaddr,cp); - batontxid = OracleBatonUtxo(CC_MARKER_VALUE,cp,oracletxid,batonaddr,mypk,prevdata); + batontxid = OracleBatonUtxo(txfee,cp,oracletxid,batonaddr,mypk,prevdata); if ( batontxid != zeroid ) // not impossible to fail, but hopefully a very rare event mtx.vin.push_back(CTxIn(batontxid,1,CScript())); else fprintf(stderr,"warning: couldnt find baton utxo %s\n",batonaddr); - if ( (inputs= AddOracleInputs(cp,mtx,oracletxid,mypk,datafee,60)) >= datafee ) + if ( (inputs= AddOracleInputs(cp,mtx,oracletxid,mypk,datafee,60)) > 0 ) { if ( inputs > datafee ) CCchange = (inputs - datafee); @@ -1043,16 +1037,15 @@ UniValue OracleFormat(uint8_t *data,int32_t datalen,char *format,int32_t formatl UniValue OracleDataSample(uint256 reforacletxid,uint256 txid) { - UniValue result(UniValue::VOBJ); CTransaction tx,oracletx; uint256 hashBlock,btxid,oracletxid; std::string error; struct CCcontract_info *cp,C; + UniValue result(UniValue::VOBJ); CTransaction tx,oracletx; uint256 hashBlock,btxid,oracletxid; std::string error; CPubKey pk; std::string name,description,format; int32_t numvouts; std::vector data; char str[67], *formatstr = 0; - cp = CCinit(&C,EVAL_ORACLES); result.push_back(Pair("result","success")); if ( myGetTransaction(reforacletxid,oracletx,hashBlock) != 0 && (numvouts=oracletx.vout.size()) > 0 ) { if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) == 'C' ) { - if ( FetchCCtx(txid,tx,cp) && (numvouts=tx.vout.size()) > 0 ) + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts=tx.vout.size()) > 0 ) { if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D' && reforacletxid == oracletxid ) { @@ -1078,9 +1071,8 @@ UniValue OracleDataSamples(uint256 reforacletxid,char* batonaddr,int32_t num) { UniValue result(UniValue::VOBJ),b(UniValue::VARR); CTransaction tx,oracletx; uint256 txid,hashBlock,btxid,oracletxid; CPubKey pk; std::string name,description,format; int32_t numvouts,n=0,vout; std::vector data; char *formatstr = 0, addr[64]; - std::vector txids; int64_t nValue; struct CCcontract_info *cp,C; - - cp = CCinit(&C,EVAL_ORACLES); + std::vector txids; int64_t nValue; + result.push_back(Pair("result","success")); if ( myGetTransaction(reforacletxid,oracletx,hashBlock) != 0 && (numvouts=oracletx.vout.size()) > 0 ) { @@ -1092,7 +1084,7 @@ UniValue OracleDataSamples(uint256 reforacletxid,char* batonaddr,int32_t num) { const CTransaction &txmempool = *it; const uint256 &hash = txmempool.GetHash(); - if ((numvouts=txmempool.vout.size())>0 && ValidateCCtx(txmempool,cp) && txmempool.vout[1].nValue==CC_MARKER_VALUE && DecodeOraclesData(txmempool.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D' && reforacletxid == oracletxid ) + if ((numvouts=txmempool.vout.size())>0 && txmempool.vout[1].nValue==CC_MARKER_VALUE && DecodeOraclesData(txmempool.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D' && reforacletxid == oracletxid ) { Getscriptaddress(addr,txmempool.vout[1].scriptPubKey); if (strcmp(addr,batonaddr)!=0) continue; @@ -1109,13 +1101,13 @@ UniValue OracleDataSamples(uint256 reforacletxid,char* batonaddr,int32_t num) } } } - SetCCtxids(txids,batonaddr,true,EVAL_ORACLES,CC_MARKER_VALUE,reforacletxid,'D'); + SetCCtxids(txids,batonaddr,true,EVAL_ORACLES,reforacletxid,'D'); if (txids.size()>0) { for (std::vector::const_iterator it=txids.end()-1; it!=txids.begin(); it--) { txid=*it; - if (FetchCCtx(txid,tx,cp) && (numvouts=tx.vout.size()) > 0 ) + if (myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts=tx.vout.size()) > 0 ) { if ( tx.vout[1].nValue==CC_MARKER_VALUE && DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D' && reforacletxid == oracletxid ) { @@ -1136,7 +1128,7 @@ UniValue OracleDataSamples(uint256 reforacletxid,char* batonaddr,int32_t num) } } else - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); } else CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); @@ -1156,8 +1148,13 @@ UniValue OracleInfo(uint256 origtxid) cp = CCinit(&C,EVAL_ORACLES); CCtxidaddr(markeraddr,origtxid); if ( myGetTransaction(origtxid,tx,hashBlock) == 0 ) - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); - else + { + fprintf(stderr,"cant find oracleid\n"); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find oracleid")); + return(result); + } + if ( myGetTransaction(origtxid,tx,hashBlock) != 0 ) { if ( tx.vout.size() > 0 && DecodeOraclesCreateOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,name,description,format) == 'C' ) { @@ -1172,7 +1169,7 @@ UniValue OracleInfo(uint256 origtxid) { txid = it->first.txhash; height = (int32_t)it->second.blockHeight; - if ( FetchCCtx(txid,tx,cp) && tx.vout.size() > 0 && + if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && DecodeOraclesOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R' && oracletxid == origtxid ) { if (publishers.find(pk)==publishers.end() || height>publishers[pk].second) @@ -1184,12 +1181,12 @@ UniValue OracleInfo(uint256 origtxid) } for (std::map>::iterator it = publishers.begin(); it != publishers.end(); ++it) { - if ( FetchCCtx(it->second.first,tx,cp) && DecodeOraclesOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R') + if ( myGetTransaction(it->second.first,tx,hashBlock) != 0 && DecodeOraclesOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R') { UniValue obj(UniValue::VOBJ); obj.push_back(Pair("publisher",pubkey33_str(str,(uint8_t *)pk.begin()))); Getscriptaddress(batonaddr,tx.vout[1].scriptPubKey); - batontxid = OracleBatonUtxo(CC_MARKER_VALUE,cp,oracletxid,batonaddr,pk,data); + batontxid = OracleBatonUtxo(10000,cp,oracletxid,batonaddr,pk,data); obj.push_back(Pair("baton",batonaddr)); obj.push_back(Pair("batontxid",uint256_str(str,batontxid))); funding = LifetimeOraclesFunds(cp,oracletxid,pk); @@ -1206,8 +1203,10 @@ UniValue OracleInfo(uint256 origtxid) result.push_back(Pair("registered",a)); } else - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); } + else + CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); return(result); } @@ -1215,7 +1214,7 @@ UniValue OraclesList() { UniValue result(UniValue::VARR); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction createtx; std::string name,description,format; char str[65]; cp = CCinit(&C,EVAL_ORACLES); - SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,CC_MARKER_VALUE,zeroid,'C'); + SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,zeroid,'C'); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { txid = *it; diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index fe97f399932..6ef33a88458 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -14,7 +14,6 @@ ******************************************************************************/ #include "CCPegs.h" -#include "CCtokens.h" #include "../importcoin.h" #include "key_io.h" #include @@ -96,13 +95,12 @@ pegs CC is able to create a coin backed (by any supported coin with gateways CC #define PEGS_ACCOUNT_YELLOW_ZONE 60 #define PEGS_ACCOUNT_RED_ZONE 90 #endif // PEGS_THRESHOLDS -#define CC_MARKER_VALUE 1000 -#define CC_TXFEE 10000 +#define CC_MARKER_VALUE 10000 extern uint64_t ASSETCHAINS_PEGSCCPARAMS[3]; extern uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,uint256 &tokenid,std::string &coin,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &gatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype); -// see include extern int64_t GetTokenBalance(CPubKey pk, uint256 tokenid); +extern int64_t GetTokenBalance(CPubKey pk, uint256 tokenid); extern int32_t komodo_currentheight(); extern int32_t prices_syntheticvec(std::vector &vec, std::vector synthetic); extern int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t minmax, int16_t leverage); @@ -127,38 +125,39 @@ uint8_t DecodePegsCreateOpRet(const CScript &scriptPubKey,std::vector & return(0); } -CScript EncodePegsAccountOpRet(uint8_t funcid,uint256 tokenid,uint256 pegstxid,CPubKey srcpub,int64_t amount,std::pair account,CPubKey accountpk) +CScript EncodePegsFundOpRet(uint256 tokenid,uint256 pegstxid,CPubKey srcpub,int64_t amount,std::pair account) { - CScript opret; uint8_t evalcode=EVAL_PEGS; struct CCcontract_info *cp,C; CPubKey pegspk; + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='F'; struct CCcontract_info *cp,C; CPubKey pegspk; std::vector pubkeys; vscript_t vopret; cp = CCinit(&C,EVAL_PEGS); pegspk = GetUnspendable(cp,0); - pubkeys.push_back(accountpk); - if (srcpub!=accountpk) pubkeys.push_back(srcpub); - vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << srcpub << amount << account << accountpk); - return(EncodeTokenOpRetV1(tokenid,pubkeys, { vopret })); + pubkeys.push_back(srcpub); + pubkeys.push_back(pegspk); + LOGSTREAM("pegscc", CCLOG_DEBUG1, stream << "EncodePegsFundOpRet [" << account.first << "," << account.second << "]" << std::endl); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << srcpub << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); } -uint8_t DecodePegsAccountOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account,CPubKey& accountpk) +uint8_t DecodePegsFundOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) { - std::vector oprets; - std::vector vopret,vOpretExtra; uint8_t *script,e,f; std::vector pubkeys; + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; - if (DecodeTokenOpRetV1(scriptPubKey,tokenid,pubkeys, oprets)!=0 && GetOpReturnCCBlob(oprets, vOpretExtra) && vOpretExtra.size()>0) + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) { vopret=vOpretExtra; } else GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account; ss >> accountpk) != 0 ) + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) { return(f); } return(0); } -uint8_t DecodePegsGetOpRet(const CTransaction tx,uint256& pegstxid,uint256 &tokenid,CPubKey &srcpub,int64_t &amount,std::pair &account,CPubKey &accountpk) +uint8_t DecodePegsGetOpRet(const CTransaction tx,uint256& pegstxid,uint256 &tokenid,CPubKey &srcpub,int64_t &amount,std::pair &account) { std::vector vopret; uint8_t *script; ImportProof proof; CTransaction burntx; std::vector payouts; @@ -166,33 +165,120 @@ uint8_t DecodePegsGetOpRet(const CTransaction tx,uint256& pegstxid,uint256 &toke GetOpReturnData(tx.vout[tx.vout.size()-1].scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0] == EVAL_IMPORTCOIN && UnmarshalImportTx(tx,proof,burntx,payouts) && UnmarshalBurnTx(burntx,pegstxid,tokenid,srcpub,amount,account,accountpk)) + if ( vopret.size() > 2 && script[0] == EVAL_IMPORTCOIN && UnmarshalImportTx(tx,proof,burntx,payouts) && UnmarshalBurnTx(burntx,pegstxid,tokenid,srcpub,amount,account)) { return('G'); } return(0); } +CScript EncodePegsReedemOpRet(uint256 tokenid,uint256 pegstxid,CPubKey srcpub,int64_t amount,std::pair account) +{ + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='R'; struct CCcontract_info *cp,C; + std::vector pubkeys; vscript_t vopret; + + cp = CCinit(&C,EVAL_PEGS); + pubkeys.push_back(srcpub); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << srcpub << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); +} + +uint8_t DecodePegsRedeemOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodePegsExchangeOpRet(uint256 tokenid,uint256 pegstxid,CPubKey pk1,CPubKey pk2,int64_t amount,std::pair account) +{ + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='E'; struct CCcontract_info *cp,C; + std::vector pubkeys; vscript_t vopret; CPubKey pegspk; + + cp = CCinit(&C,EVAL_PEGS); + pegspk = GetUnspendable(cp,0); + pubkeys.push_back(pk1); + pubkeys.push_back(pk2); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << pk1 << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); +} + +uint8_t DecodePegsExchangeOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodePegsLiquidateOpRet(uint256 tokenid,uint256 pegstxid,CPubKey srcpub,int64_t amount,std::pair account) +{ + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='L'; struct CCcontract_info *cp,C; + std::vector pubkeys; vscript_t vopret; + + cp = CCinit(&C,EVAL_PEGS); + pubkeys.push_back(srcpub); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << srcpub << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); +} + +uint8_t DecodePegsLiquidateOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) + { + return(f); + } + return(0); +} + uint8_t DecodePegsOpRet(CTransaction tx,uint256& pegstxid,uint256& tokenid) { - std::vector oprets; int32_t numvouts=tx.vout.size(); - std::vector vopret,vOpretExtra; uint8_t *script,e,f; std::vector pubkeys; - ImportProof proof; CTransaction burntx; std::vector payouts; uint256 tmppegstxid; CPubKey srcpub,accountpk; int64_t amount; std::pair account; + std::vector> oprets; int32_t numvouts=tx.vout.size(); + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + ImportProof proof; CTransaction burntx; std::vector payouts; uint256 tmppegstxid; CPubKey srcpub; int64_t amount; std::pair account; - if (numvouts<1) return 0; - if (DecodeTokenOpRetV1(tx.vout[numvouts-1].scriptPubKey,tokenid,pubkeys, oprets)!=0 && GetOpReturnCCBlob(oprets, vOpretExtra) && vOpretExtra.size()>0) + if (DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) { vopret=vOpretExtra; } else GetOpReturnData(tx.vout[numvouts-1].scriptPubKey, vopret); script = (uint8_t *)vopret.data(); if (tx.IsPegsImport()) - return(DecodePegsGetOpRet(tx,pegstxid,tokenid,srcpub,amount,account,accountpk)); + return(DecodePegsGetOpRet(tx,pegstxid,tokenid,srcpub,amount,account)); else if ( vopret.size() > 2 && script[0] == EVAL_PEGS) { E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid); - if (f == 'C' || f == 'F' || f == 'R' || f == 'X' || f == 'E' || f == 'L') - return(f); + return(f); } return(0); } @@ -246,7 +332,125 @@ bool PegsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction & else return(true); } -std::string PegsDecodeAccountTx(CTransaction tx,CPubKey& pk,int64_t &amount,std::pair &account,CPubKey &accountpk) +bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) +{ + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; + return (true); + std::vector > txids; + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + preventCCvins = preventCCvouts = -1; + if ( numvouts < 1 ) + return eval->Invalid("no vouts"); + else + { + for (i=0; iInvalid("illegal normal vini"); + } + } + //fprintf(stderr,"check amounts\n"); + if ( PegsExactAmounts(cp,eval,tx,1,10000) == false ) + { + fprintf(stderr,"Pegsget invalid amount\n"); + return false; + } + else + { + txid = tx.GetHash(); + memcpy(hash,&txid,sizeof(hash)); + retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); + if ( retval != 0 ) + fprintf(stderr,"Pegsget validated\n"); + else fprintf(stderr,"Pegsget invalid\n"); + return(retval); + } + } +} +// end of consensus code + +// helper functions for rpc calls in rpcwallet.cpp + +int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk1,CPubKey pk2,int64_t total,int32_t maxinputs) +{ + // add threshold check + char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + std::vector > unspentOutputs; + + if (pk2.IsValid()) GetCCaddress1of2(cp,coinaddr,pk1,pk2); + else GetCCaddress(cp,coinaddr,pk1); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + // no need to prevent dup + if ( myGetTransaction(txid,vintx,hashBlock) != 0 ) + { + if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + { + if ( total != 0 && maxinputs != 0 ) + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + nValue = it->second.satoshis; + totalinputs += nValue; + n++; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; + } + } + } + return(totalinputs); +} + +int64_t AddPegsTokenInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,uint256 pegstxid, uint256 tokenid, CPubKey pk1,CPubKey pk2, int64_t total,int32_t maxinputs) +{ + // add threshold check + char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + std::vector > unspentOutputs; uint256 tmppegstxid,tmptokenid; CPubKey mypk; + + if (pk2.IsValid()) GetTokensCCaddress1of2(cp,coinaddr,pk1,pk2); + else GetTokensCCaddress(cp,coinaddr,pk1); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + // no need to prevent dup + if ( myGetTransaction(txid,vintx,hashBlock) != 0 ) + { + if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 && DecodePegsOpRet(vintx,tmppegstxid,tmptokenid)!=0 && tmppegstxid==pegstxid && tmptokenid==tokenid) + { + if ( total != 0 && maxinputs != 0 ) + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + nValue = it->second.satoshis; + totalinputs += nValue; + n++; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; + } + } + } + if (pk2.IsValid()) + { + mypk = pubkey2pk(Mypubkey()); + if (mypk!=pk1 && mypk!=pk2) + { + CCaddrTokens1of2set(cp,pk1,pk2,cp->CCpriv,coinaddr); + } + else + { + uint8_t mypriv[32]; + Myprivkey(mypriv); + CCaddrTokens1of2set(cp,pk1,pk2,mypriv,coinaddr); + memset(mypriv,0,sizeof(mypriv)); + } + } + return(totalinputs); +} + +std::string PegsDecodeAccountTx(CTransaction tx,CPubKey& pk,int64_t &amount,std::pair &account) { uint256 hashBlock,tokenid,pegstxid; int32_t numvouts=tx.vout.size(); char funcid; @@ -254,17 +458,15 @@ std::string PegsDecodeAccountTx(CTransaction tx,CPubKey& pk,int64_t &amount,std: { switch(funcid) { - case 'F': if (DecodePegsAccountOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account,accountpk)=='F') return("fund"); + case 'F': if (DecodePegsFundOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='F') return("fund"); break; - case 'G': if (DecodePegsGetOpRet(tx,pegstxid,tokenid,pk,amount,account,accountpk)=='G') return("get"); + case 'G': if (DecodePegsGetOpRet(tx,pegstxid,tokenid,pk,amount,account)=='G') return("get"); break; - case 'R': if (DecodePegsAccountOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account,accountpk)=='R') return("redeem"); + case 'R': if (DecodePegsRedeemOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='R') return("redeem"); break; - case 'X': if (DecodePegsAccountOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account,accountpk)=='X') return("close"); + case 'E': if (DecodePegsExchangeOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='R') return("exchange"); break; - case 'E': if (DecodePegsAccountOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account,accountpk)=='E') return("exchange"); - break; - case 'L': if (DecodePegsAccountOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account,accountpk)=='L') return("liquidate"); + case 'L': if (DecodePegsLiquidateOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='L') return("liquidate"); break; } } @@ -274,7 +476,7 @@ std::string PegsDecodeAccountTx(CTransaction tx,CPubKey& pk,int64_t &amount,std: char PegsFindAccount(struct CCcontract_info *cp,CPubKey pk,uint256 pegstxid, uint256 tokenid, uint256 &accounttxid, std::pair &account) { char coinaddr[64]; int64_t nValue,tmpamount; uint256 txid,spenttxid,hashBlock,tmptokenid,tmppegstxid; - CTransaction tx,acctx; int32_t numvouts,vout,ratio; char funcid,f; CPubKey pegspk,tmppk,accountpk; + CTransaction tx,acctx; int32_t numvouts,vout,ratio; char funcid,f; CPubKey pegspk,tmppk; std::vector > unspentOutputs; ImportProof proof; CTransaction burntx; std::vector payouts; @@ -296,9 +498,20 @@ char PegsFindAccount(struct CCcontract_info *cp,CPubKey pk,uint256 pegstxid, uin acctx=tx; } } + if (accounttxid!=zeroid && myIsutxo_spentinmempool(spenttxid,ignorevin,accounttxid,1) != 0) + { + accounttxid=zeroid; + if (myGetTransaction(spenttxid,tx,hashBlock)!=0 && (numvouts=tx.vout.size()) > 0 && + (f=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid && tokenid==tmptokenid) + { + funcid=f; + accounttxid=spenttxid; + acctx=tx; + } + } if (accounttxid!=zeroid) { - PegsDecodeAccountTx(acctx,tmppk,tmpamount,account,accountpk); + PegsDecodeAccountTx(acctx,tmppk,tmpamount,account); return(funcid); } else return(0); @@ -309,7 +522,7 @@ int64_t PegsGetTokenPrice(uint256 tokenid) int64_t price; CTransaction tokentx; uint256 hashBlock; std::vector exp; std::string name,desc; std::vector vorigpubkey; int32_t numvouts; - if (myGetTransaction(tokenid,tokentx,hashBlock)!=0 && (numvouts=tokentx.vout.size())>0 && DecodeTokenCreateOpRetV1(tokentx.vout[numvouts-1].scriptPubKey,vorigpubkey,name,desc)=='c') + if (myGetTransaction(tokenid,tokentx,hashBlock)!=0 && (numvouts=tokentx.vout.size())>0 && DecodeTokenCreateOpRet(tokentx.vout[numvouts-1].scriptPubKey,vorigpubkey,name,desc)=='c') { std::vector vexpr; SplitStr(desc, vexpr); @@ -323,27 +536,25 @@ std::string PegsGetTokenName(uint256 tokenid) { CTransaction tokentx; uint256 hashBlock; std::string name,desc; std::vector vorigpubkey; int32_t numvouts; - if (myGetTransaction(tokenid,tokentx,hashBlock)!=0 && (numvouts=tokentx.vout.size())>0 && DecodeTokenCreateOpRetV1(tokentx.vout[numvouts-1].scriptPubKey,vorigpubkey,name,desc)=='c') + if (myGetTransaction(tokenid,tokentx,hashBlock)!=0 && (numvouts=tokentx.vout.size())>0 && DecodeTokenCreateOpRet(tokentx.vout[numvouts-1].scriptPubKey,vorigpubkey,name,desc)=='c') { return (name); } - LOGSTREAM("pegscc",CCLOG_ERROR, stream << "cant find token create or invalid tokenid " << tokenid.GetHex() << std::endl); + CCerror = strprintf("cant find token create or invalid tokenid %s",tokenid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } int64_t PegsGetTokensAmountPerPrice(int64_t amount,uint256 tokenid) { mpz_t res,a,b; - int64_t price=PegsGetTokenPrice(tokenid); - - if (price==0) return (0); mpz_init(res); mpz_init(a); mpz_init(b); mpz_set_si(a, amount); mpz_set_si(b, COIN); mpz_mul(res, a, b); - mpz_set_si(a, price); + mpz_set_si(a, PegsGetTokenPrice(tokenid)); mpz_tdiv_q(res, res, a); return (mpz_get_si(res)); } @@ -365,14 +576,14 @@ double PegsGetRatio(uint256 tokenid,std::pair account) double PegsGetAccountRatio(uint256 pegstxid,uint256 tokenid,uint256 accounttxid) { int64_t amount; uint256 hashBlock,tmptokenid,tmppegstxid; - CTransaction tx; int32_t numvouts; char funcid; CPubKey pk,accountpk; + CTransaction tx; int32_t numvouts; char funcid; CPubKey pk; std::pair account; struct CCcontract_info *cp,C; cp = CCinit(&C,EVAL_PEGS); if (myGetTransaction(accounttxid,tx,hashBlock) != 0 && (numvouts=tx.vout.size())>0 && (funcid=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid && tokenid==tmptokenid) { - PegsDecodeAccountTx(tx,pk,amount,account,accountpk); + PegsDecodeAccountTx(tx,pk,amount,account); return PegsGetRatio(tokenid,account); } return (0); @@ -381,7 +592,7 @@ double PegsGetAccountRatio(uint256 pegstxid,uint256 tokenid,uint256 accounttxid) double PegsGetGlobalRatio(uint256 pegstxid) { char coinaddr[64]; int64_t nValue,amount,globaldebt=0; uint256 txid,accounttxid,hashBlock,tmppegstxid,tokenid; - CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk,accountpk; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk; std::vector > unspentOutputs; std::pair account; std::map> globalaccounts; struct CCcontract_info *cp,C; @@ -398,7 +609,7 @@ double PegsGetGlobalRatio(uint256 pegstxid) if (vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts=tx.vout.size())>0 && (funcid=DecodePegsOpRet(tx,tmppegstxid,tokenid))!=0 && pegstxid==tmppegstxid && (funcid=='F' || funcid=='G' || funcid=='E')) { - PegsDecodeAccountTx(tx,pk,amount,account,accountpk); + PegsDecodeAccountTx(tx,pk,amount,account); globalaccounts[tokenid].first+=account.first; globalaccounts[tokenid].second+=account.second; } @@ -436,15 +647,16 @@ double PegsGetGlobalRatio(uint256 pegstxid) mpz_set_si(res, 0); mpz_set_si(a, COIN); mpz_tdiv_q(res, globaldeposit, a); + printf("%lu %lu\n",globaldebt,mpz_get_si(res)); return ((double)globaldebt)*100/mpz_get_si(res); } return (0); } -std::string PegsFindSuitableAccount(struct CCcontract_info *cp,uint256 pegstxid, uint256 tokenid, int64_t tokenamount,uint256 &accounttxid, std::pair &account) +std::string PegsFindBestAccount(struct CCcontract_info *cp,uint256 pegstxid, uint256 tokenid, int64_t tokenamount,uint256 &accounttxid, std::pair &account) { char coinaddr[64]; int64_t nValue,tmpamount; uint256 txid,hashBlock,tmptokenid,tmppegstxid; - CTransaction tx,acctx; int32_t numvouts,vout; char funcid,f; CPubKey pegspk,tmppk,accountpk; + CTransaction tx,acctx; int32_t numvouts,vout; char funcid,f; CPubKey pegspk,tmppk; std::vector > unspentOutputs; ImportProof proof; CTransaction burntx; std::vector payouts; double ratio,maxratio=0; std::pair tmpaccount; @@ -460,9 +672,9 @@ std::string PegsFindSuitableAccount(struct CCcontract_info *cp,uint256 pegstxid, nValue = (int64_t)it->second.satoshis; LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "txid=" << txid.GetHex() << ", vout=" << vout << ", nValue=" << nValue << std::endl); if (vout == 0 && nValue == CC_MARKER_VALUE && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,0) == 0 && - (ratio=PegsGetAccountRatio(pegstxid,tokenid,txid))>=(ASSETCHAINS_PEGSCCPARAMS[2]?ASSETCHAINS_PEGSCCPARAMS[2]:PEGS_ACCOUNT_YELLOW_ZONE) && ratio>maxratio) + (ratio=PegsGetAccountRatio(pegstxid,tokenid,txid))>(ASSETCHAINS_PEGSCCPARAMS[2]?ASSETCHAINS_PEGSCCPARAMS[2]:PEGS_ACCOUNT_YELLOW_ZONE) && ratio>maxratio) { - if (myGetTransaction(txid,tx,hashBlock)!=0 && !PegsDecodeAccountTx(tx,tmppk,tmpamount,tmpaccount,accountpk).empty() && tmpaccount.first>=tokenamount) + if (myGetTransaction(txid,tx,hashBlock)!=0 && !PegsDecodeAccountTx(tx,tmppk,tmpamount,tmpaccount).empty() && tmpaccount.first>=tokenamount) { accounttxid=txid; acctx=tx; @@ -470,362 +682,37 @@ std::string PegsFindSuitableAccount(struct CCcontract_info *cp,uint256 pegstxid, } } } - if (!maxratio) - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - nValue = (int64_t)it->second.satoshis; - LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "txid=" << txid.GetHex() << ", vout=" << vout << ", nValue=" << nValue << std::endl); - if (vout == 0 && nValue == CC_MARKER_VALUE && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,0) == 0 && (ratio=PegsGetAccountRatio(pegstxid,tokenid,txid))>maxratio) - { - if (myGetTransaction(txid,tx,hashBlock)!=0 && !PegsDecodeAccountTx(tx,tmppk,tmpamount,tmpaccount,accountpk).empty() && tmpaccount.first>=tokenamount) - { - accounttxid=txid; - acctx=tx; - maxratio=ratio; - } - } - } if (accounttxid!=zeroid) { - return(PegsDecodeAccountTx(acctx,tmppk,tmpamount,account,accountpk)); + return(PegsDecodeAccountTx(acctx,tmppk,tmpamount,account)); } else return(""); } -bool CheckSynthetic(std::string description) -{ - std::vector vexpr; std::vector exp; - - SplitStr(description, vexpr); - if (prices_syntheticvec(exp, vexpr)<0) return (false); - return (true); -} - -std::string ValidateAccount(const CTransaction &tx, const uint256 &tokenid,const std::pair &prevaccount) -{ - struct CCcontract_info *cp,C; CPubKey pegspk,pk,accountpk; char addr[64]; int64_t amount; std::pair account(0,0); - std::string name=PegsDecodeAccountTx(tx,pk,amount,account,accountpk); - - cp = CCinit(&C,EVAL_PEGS); - pegspk=GetUnspendable(cp,0); - if ( (*cp->ismyvin)(tx.vin[0].scriptSig) == 0 ) - return ("vin.0 is CC account marker for pegs"+name+"!"); - else if ( (*cp->ismyvin)(tx.vin[1].scriptSig) == 0 ) - return ("vin.1 is CC account marker for pegs"+name+"!"); - else if ( GetCCaddress1of2(cp,addr,pegspk,pegspk) && ConstrainVout(tx.vout[0],1,addr,CC_MARKER_VALUE)==0) - return ("invalid account marker vout.0 for pegs"+name+"!"); - else if ( GetCCaddress1of2(cp,addr,accountpk,pegspk) && ConstrainVout(tx.vout[1],1,addr,CC_MARKER_VALUE)==0) - return ("invalid account marker vout.1 for pegs"+name+"!"); - else if (name=="fund" && (prevaccount.first+amount!=account.first || prevaccount.second!=account.second || pk!=accountpk)) - return ("invalid previous and current account comparisons!"); - else if (name=="redeem" && (prevaccount.first-amount!=account.first || prevaccount.second!=account.second || pk!=accountpk)) - return ("invalid previous and current account comparisons!"); - else if (name=="close" && (account.first!=0 || prevaccount.second-amount!=0 || account.second!=0 || pk!=accountpk)) - return ("invalid previous and current account comparisons!"); - else if (name=="exchange" && (prevaccount.first-PegsGetTokensAmountPerPrice(amount,tokenid)!=account.first || prevaccount.second-amount!=account.second)) - return ("invalid previous and current account comparisons!"); - else if (name=="liquidate" && (prevaccount.second-amount!=account.second || account.first!=0 || account.second!=0)) - return ("invalid previous and current account comparisons!"); - return (""); -} - -bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) -{ - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid,pegstxid,tokenid,accounttxid,tmpaccounttxid,hashBlock; - uint8_t funcid; char str[65],destaddr[64],addr[64]; int64_t amount; std::pair account(0,0),prevaccount(0,0),tmpaccount(0,0); - CPubKey srcpub,pegspk,accountpk; std::string error,name,description; std::vector bindtxids; CTransaction tmptx; std::vector vorigpubkey; - - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) - return eval->Invalid("no vouts"); - else - { - // if ( PegsExactAmounts(cp,eval,tx,1,10000) == false ) - // { - // fprintf(stderr,"Pegsget invalid amount\n"); - // return false; - // } - // else - // { - txid = tx.GetHash(); - CCOpretCheck(eval,tx,true,true,true); - ExactAmounts(eval,tx,CC_TXFEE); - if ((funcid=DecodePegsOpRet(tx,pegstxid,tokenid)) !=0 ) - { - pegspk=GetUnspendable(cp,0); - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - return eval->Invalid("invalid pegs txid, for this chain only valid pegs txid is"+KOMODO_EARLYTXID.GetHex()); - switch (funcid) - { - case 'C': - //vin.0: normal input - //vout.0-99: CC vouts for pegs funds - //vout.1: CC vout marker - //vout.n-1: opreturn - 'B' tokenid coin totalsupply oracletxid M N pubkeys taddr prefix prefix2 wiftype - return eval->Invalid("unexpected PegsValidate for pegscreate!"); - break; - case 'F': - //if account exists: - //vin.0: marker input from account - //vin.1: marker input from account - //else - //vin.0: input from pegsCC global address - //vout.0: CC vout account marker (1of2 of mypk and pegspk) - //vout.1: CC vout account marker (1of2 of pegspk and pegspk)if (myGetTransaction(pegstxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - //vout.2: tokens to 1of2 address of mypk and pegspk - //vout.3: CC change of tokens if exists - //vout.4: CC change back to pegs global address if exists - //vout.n-1: opreturn - 'F' tokenid pegstxid mypk amount account - if (myGetTransaction(pegstxid,tmptx,hashBlock)==0 || (numvouts=tmptx.vout.size())<=0) - return eval->Invalid("invalid pegs txid!"); - else if (DecodePegsCreateOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') - return eval->Invalid("invalid pegscreate OP_RETURN data!"); - else if (myGetTransaction(tokenid,tmptx,hashBlock)==0 || (numvouts=tmptx.vout.size())<=0) - return eval->Invalid("invalid token id!"); - else if (DecodeTokenCreateOpRetV1(tmptx.vout[numvouts-1].scriptPubKey,vorigpubkey,name,description)!='c') - return eval->Invalid("invalid token OP_RETURN data!"); - else if (!CheckSynthetic(description)) - return eval->Invalid("invalid synthetic in token description field. You must put the price synthetic in token description field!"); - else if ((numvouts=tx.vout.size()) < 1 || DecodePegsAccountOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,srcpub,amount,account,accountpk)!='F') - return eval->Invalid("invalid pegsfund OP_RETURN data!"); - else if (PegsFindAccount(cp,srcpub,pegstxid,tokenid,accounttxid,prevaccount)!=0 && !(error=ValidateAccount(tx,tokenid,prevaccount)).empty()) - return eval->Invalid(error); - else if (accounttxid==zeroid) - { - if ((*cp->ismyvin)(tx.vin[0].scriptSig) == 0 ) - return eval->Invalid("vin.0 is CC for pegsfund!"); - else if ( GetCCaddress1of2(cp,addr,pegspk,pegspk) && ConstrainVout(tx.vout[0],1,addr,CC_MARKER_VALUE)==0) - return eval->Invalid("invalid account marker vout.0 for pegsfund!"); - else if ( GetCCaddress1of2(cp,addr,srcpub,pegspk) && ConstrainVout(tx.vout[1],1,addr,CC_MARKER_VALUE)==0) - return eval->Invalid("invalid account marker vout.1 for pegsfund!"); - else if (amount!=account.first || account.second!=0) - return eval->Invalid("different amount and account state!"); - } - else if (GetTokensCCaddress1of2(cp,addr,srcpub,pegspk) && ConstrainVout(tx.vout[2],1,addr,amount)==0) - return ("invalid tokens destination or amount vout.2 for pegsfund!"); - break; - case 'R': - //vin.0: marker input from account - //vin.1: marker input from account - //vout.0: CC vout account marker (1of2 of mypk and pegspk) - //vout.1: CC vout account marker (1of2 of pegspk and pegspk) - //vout.n-1: opreturn - 'R' tokenid pegstxid mypk amount account - if ((numvouts=tx.vout.size()) < 1 || DecodePegsAccountOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,srcpub,amount,account,accountpk)!='R') - return eval->Invalid("invalid pegsredeem OP_RETURN data!"); - else if (PegsFindAccount(cp,srcpub,pegstxid,tokenid,accounttxid,prevaccount)==0) - return eval->Invalid("no account found to redeem from, please create account first with pegsfund!"); - else if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0 && ignoretxid!=tx.GetHash()) - return eval->Invalid("previous account tx not yet confirmed!"); - else if (!(error=ValidateAccount(tx,tokenid,prevaccount)).empty()) - return eval->Invalid(error); - else if (PegsGetRatio(tokenid,account)>=PEGS_ACCOUNT_MAX_DEBT) - return eval->Invalid(std::string("cannot redeem when account ratio >= ") + std::to_string(PEGS_ACCOUNT_MAX_DEBT) + "%%!"); - else if (amount>account.first-(PegsGetTokensAmountPerPrice(account.second,tokenid)*100/PEGS_ACCOUNT_MAX_DEBT)) - return eval->Invalid(std::string("cannot redeem this amount of tokens, you must leave enough tokens to have account ratio <= ") + std::to_string(PEGS_ACCOUNT_MAX_DEBT) + "%%!"); - else if (_GetCCaddress(addr,EVAL_TOKENS,srcpub) && ConstrainVout(tx.vout[2],1,addr,amount)==0) - return ("invalid tokens destination or amount vout for pegsredeem!"); - else if (numvouts>3 && GetTokensCCaddress1of2(cp,addr,srcpub,pegspk) && ConstrainVout(tx.vout[3],1,addr,prevaccount.first-amount)==0) - return ("invalid tokens destination or amount vout for pegsredeem!"); - break; - case 'X': - //vin.0: marker input from account - //vin.1: marker input from account - //vout.0: CC vout account marker (1of2 of mypk and pegspk) - //vout.1: CC vout account marker (1of2 of pegspk and pegspk) - //vout.n-1: opreturn - 'X' tokenid pegstxid mypk amount account - if ((numvouts=tx.vout.size()) < 1 || DecodePegsAccountOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,srcpub,amount,account,accountpk)!='X') - return eval->Invalid("invalid pegsclose OP_RETURN data!"); - else if (PegsFindAccount(cp,srcpub,pegstxid,tokenid,accounttxid,prevaccount)==0) - return eval->Invalid("no account found to close, please create account first with pegsfund!"); - else if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0 && ignoretxid!=tx.GetHash()) - return eval->Invalid("previous account tx not yet confirmed!"); - else if (!(error=ValidateAccount(tx,tokenid,prevaccount)).empty()) - return eval->Invalid(error); - else if (_GetCCaddress(addr,EVAL_TOKENS,srcpub) && ConstrainVout(tx.vout[2],1,addr,prevaccount.first)==0) - return ("invalid tokens destination or amount vout.2 for pegsclose!"); - else if ( Getscriptaddress(addr,CScript() << ParseHex(HexStr(CCtxidaddr(addr,pegstxid))) << OP_CHECKSIG) && ConstrainVout(tx.vout[3],0,addr,prevaccount.second)==0) - return ("invalid coins destination or amount vout.3 for pegsclose!"); - break; - case 'E': - //vin.0: marker input from account - //vin.1: marker input from account - //vout.0: CC vout account marker (1of2 of mypk and pegspk) - //vout.1: CC vout account marker (1of2 of pegspk and pegspk) - //vout.n-1: opreturn - 'E' tokenid pegstxid mypk amount account - if ((numvouts=tx.vout.size()) < 1 || DecodePegsAccountOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,srcpub,amount,account,accountpk)!='E') - return eval->Invalid("invalid pegsexchange OP_RETURN data!"); - else if (PegsFindAccount(cp,accountpk,pegstxid,tokenid,accounttxid,prevaccount)==0) - return eval->Invalid("no account found to exchange coins!"); - else if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0 && ignoretxid!=tx.GetHash()) - return eval->Invalid("previous account tx not yet confirmed!"); - else if (!(error=ValidateAccount(tx,tokenid,prevaccount)).empty()) - return eval->Invalid(error); - else if (PegsGetAccountRatio(pegstxid,tokenid,accounttxid)<(ASSETCHAINS_PEGSCCPARAMS[2]?ASSETCHAINS_PEGSCCPARAMS[2]:PEGS_ACCOUNT_YELLOW_ZONE)) - return eval->Invalid("cannot exchange coins from account that is not yellow zone!"); - else if ((PegsFindSuitableAccount(cp,pegstxid,tokenid,amount,tmpaccounttxid,tmpaccount)).empty() || tx.vin[0].prevout.hash!=tmpaccounttxid || tx.vin[1].prevout.hash!=tmpaccounttxid) - return eval->Invalid("cannot exchange from this account, it is not worst account there is!"); - else if (_GetCCaddress(addr,EVAL_TOKENS,srcpub) && ConstrainVout(tx.vout[2],1,addr,prevaccount.first-account.first)==0) - return ("invalid tokens destination or amount vout.2 for pegsexchange!"); - else if (Getscriptaddress(addr,CScript() << ParseHex(HexStr(CCtxidaddr(addr,pegstxid))) << OP_CHECKSIG) && ConstrainVout(tx.vout[3],0,addr,amount)==0) - return ("invalid coins destination or amount vout.3 for pegsexchange, it should be coin burn vout!"); - else if (numvouts>4 && GetTokensCCaddress1of2(cp,addr,accountpk,pegspk) && ConstrainVout(tx.vout[4],1,addr,account.first)==0) - return ("invalid tokens destination or amount vout.4 for pegsexchange, it should be the change of tokens back to account address!"); - else if (numvouts>5 && GetCCaddress(cp,addr,pegspk) && ConstrainVout(tx.vout[5],1,addr,0)==0) - return ("invalid coins destination or amount vout.5 for pegsexchange, it should be change back to pegs CC global address!"); - break; - case 'L': - //vin.0: marker input from account - //vin.1: marker input from account - //vout.0: CC vout account marker (1of2 of mypk and pegspk) - //vout.1: CC vout account marker (1of2 of pegspk and pegspk) - //vout.n-1: opreturn - 'L' tokenid pegstxid mypk amount account - if ((numvouts=tx.vout.size()) < 1 || DecodePegsAccountOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,srcpub,amount,account,accountpk)!='L') - return eval->Invalid("invalid pegsliquidate OP_RETURN data!"); - else if (PegsFindAccount(cp,accountpk,pegstxid,tokenid,accounttxid,prevaccount)==0) - return eval->Invalid("cannot find the account to liquidate!"); - else if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0 && ignoretxid!=tx.GetHash()) - return eval->Invalid("previous liquidation account tx not yet confirmed"); - else if (!(error=ValidateAccount(tx,tokenid,prevaccount)).empty()) - return eval->Invalid(error); - else if (PegsGetRatio(tokenid,prevaccount)<(ASSETCHAINS_PEGSCCPARAMS[0]?ASSETCHAINS_PEGSCCPARAMS[0]:PEGS_ACCOUNT_RED_ZONE)) - return eval->Invalid("cannot liquidate account that is not in the red zone!"); - else if (_GetCCaddress(addr,EVAL_TOKENS,srcpub) && ConstrainVout(tx.vout[2],1,addr,amount)==0) - return ("invalid tokens destination or amount vout.2 for pegsliquidate!"); - else if (Getscriptaddress(addr,CScript() << ParseHex(HexStr(CCtxidaddr(addr,pegstxid))) << OP_CHECKSIG) && ConstrainVout(tx.vout[3],0,addr,prevaccount.second)==0) - return ("invalid coins destination or amount vout.3 for pegsliquidate, it should be coin burn vout!"); - else if (GetTokensCCaddress(cp,addr,pegspk) && ConstrainVout(tx.vout[4],1,addr,prevaccount.first-amount)==0) - return ("invalid tokens destination or amount vout.4 for pegsliquidate, it should be the rest of tokens to pegs CC global tokens address!"); - else if (numvouts>5 && GetCCaddress(cp,addr,pegspk) && ConstrainVout(tx.vout[5],1,addr,0)==0) - return ("invalid coins destination or amount vout.5 for pegsliquidate, it should be change back to pegs CC global address!"); - break; - } - } - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); - if ( retval != 0 ) - fprintf(stderr,"Pegs tx validated\n"); - else fprintf(stderr,"Pegs tx invalid\n"); - return(retval); - // } - } -} -// end of consensus code - -// helper functions for rpc calls in rpcwallet.cpp - -int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk1,CPubKey pk2,int64_t total,int32_t maxinputs) -{ - // add threshold check - char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; - std::vector > unspentOutputs; - - if (pk2.IsValid()) GetCCaddress1of2(cp,coinaddr,pk1,pk2); - else GetCCaddress(cp,coinaddr,pk1); - SetCCunspents(unspentOutputs,coinaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - // no need to prevent dup - if ( myGetTransaction(txid,vintx,hashBlock) != 0 ) - { - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) - { - if ( total != 0 && maxinputs != 0 ) - { - mtx.vin.push_back(CTxIn(txid,vout,CScript())); - nValue = it->second.satoshis; - totalinputs += nValue; - n++; - } - if ( totalinputs >= total || (maxinputs > 0 && n >= maxinputs) ) - break; - } - } - } - return(totalinputs); -} - -int64_t AddPegsTokenInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,uint256 pegstxid, uint256 tokenid, CPubKey pk1,CPubKey pk2, int64_t total,int32_t maxinputs) -{ - // add threshold check - char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; - std::vector > unspentOutputs; uint256 tmppegstxid,tmptokenid; CPubKey mypk; - - if (pk2.IsValid()) GetTokensCCaddress1of2(cp,coinaddr,pk1,pk2); - else GetTokensCCaddress(cp,coinaddr,pk1); - SetCCunspents(unspentOutputs,coinaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - // no need to prevent dup - if ( myGetTransaction(txid,vintx,hashBlock) != 0 ) - { - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 && DecodePegsOpRet(vintx,tmppegstxid,tmptokenid)!=0 && tmppegstxid==pegstxid && tmptokenid==tokenid) - { - if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,vout,CScript())); - nValue = it->second.satoshis; - totalinputs += nValue; - n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) - break; - } - } - } - if (pk2.IsValid()) - { - mypk = pubkey2pk(Mypubkey()); - if (mypk!=pk1 && mypk!=pk2) - { - CCaddrTokens1of2set(cp,pk1,pk2,cp->CCpriv,coinaddr); - } - else - { - uint8_t mypriv[32]; - Myprivkey(mypriv); - CCaddrTokens1of2set(cp,pk1,pk2,mypriv,coinaddr); - memset(mypriv,0,sizeof(mypriv)); - } - } - return(totalinputs); -} - UniValue PegsCreate(const CPubKey& pk,uint64_t txfee,int64_t amount, std::vector bindtxids) { - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::vector vorigpubkey; - CPubKey mypk,pegspk; struct CCcontract_info *cp,C; CTransaction tx; int32_t numvouts; int64_t totalsupply; std::string coin,name,description; + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,pegspk; struct CCcontract_info *cp,C; CTransaction tx; int32_t numvouts; int64_t totalsupply; std::string coin; char depositaddr[64]; uint256 txid,hashBlock,tmptokenid,oracletxid; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; cp = CCinit(&C,EVAL_PEGS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_PEGS]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); pegspk = GetUnspendable(cp,0); for(auto txid : bindtxids) { if (myGetTransaction(txid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find bindtxid " << txid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find bindtxid " << txid.GetHex()); if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid bindtxid " << txid.GetHex()); - if (myGetTransaction(tmptokenid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find tokenid " << txid.GetHex()); - else if (DecodeTokenCreateOpRetV1(tx.vout[numvouts-1].scriptPubKey,vorigpubkey,name,description)!='c') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid token OP_RETURN data!"); - else if (!CheckSynthetic(description)) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid synthetic in token description field. You must put the price synthetic in token description field!"); - - } - if ( AddNormalinputs(mtx,mypk,amount+txfee,64,pk.IsValid()) >= amount+txfee ) + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid bindtxid " << txid.GetHex()); + } + if ( AddNormalinputs(mtx,mypk,amount,64,pk.IsValid()) >= amount ) { for (int i=0; i<100; i++) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,(amount-txfee)/100,pegspk)); return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodePegsCreateOpRet(bindtxids))); } - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "error adding normal inputs"); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "error adding normal inputs"); } UniValue PegsFund(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid,int64_t amount) @@ -838,21 +725,19 @@ UniValue PegsFund(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tok cp = CCinit(&C,EVAL_PEGS); cpTokens = CCinit(&CTokens,EVAL_TOKENS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_PEGS]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); pegspk = GetUnspendable(cp,0); - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid. On this chain only valid pegstxid is " << KOMODO_EARLYTXID.GetHex()); if (myGetTransaction(pegstxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find pegstxid " << pegstxid.GetHex()); if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid pegstxid " << pegstxid.GetHex()); for(auto txid : bindtxids) { if (myGetTransaction(txid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find bindtxid " << txid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find bindtxid " << txid.GetHex()); if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid bindtxid " << txid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid bindtxid " << txid.GetHex()); if (tmptokenid==tokenid) { found=true; @@ -860,21 +745,19 @@ UniValue PegsFund(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tok } } if (!found) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid tokenid " << tokenid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid tokenid " << tokenid.GetHex()); if ((balance=GetTokenBalance(mypk,tokenid))>=amount) { PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account); LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "previous account tx not yet confirmed"); - if (accounttxid!=zeroid) + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "previous account tx not yet confirmed"); + if (accounttxid!=zeroid && (funds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) { - mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); - mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); - if (txfee>0 && (funds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,funds-(txfee+2*CC_MARKER_VALUE),pegspk)); account.first+=amount; LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodePegsAccountOpRet('F',tokenid,pegstxid,mypk,amount,account,mypk))); + return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodePegsFundOpRet(tokenid,pegstxid,mypk,amount,account))); } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream <<"not enough tokens in pegs global tokens CC address"); } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream <<"not enough balance in pegs global CC address"); + else + CCERR_RESULT("pegscc",CCLOG_INFO, stream <<"not enough balance in pegs global CC address"); } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not enough balance (" << balance << ") for this amount of tokens " << amount); - return NullUniValue; + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not enough balance (" << balance << ") for this amount of tokens " << amount); } UniValue PegsGet(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount) @@ -910,19 +792,17 @@ UniValue PegsGet(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 toke cp = CCinit(&C,EVAL_PEGS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_PEGS]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); pegspk = GetUnspendable(cp,0); - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid. On this chain only valid pegstxid is " << KOMODO_EARLYTXID.GetHex()); if (myGetTransaction(pegstxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find pegstxid " << pegstxid.GetHex()); if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid pegstxid " << pegstxid.GetHex()); if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account)==0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cannot find account from which to issue coins, fund account first with pegsfund!"); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cannot find account from which to issue coins, fund account first with pegsfund!"); if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "previous account tx not yet confirmed"); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "previous account tx not yet confirmed"); LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); // spending markers vouts.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); @@ -930,19 +810,18 @@ UniValue PegsGet(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 toke // coin issue vouts.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); account.second+=amount; -#ifndef TESTMODE_PEGS if (PegsGetRatio(tokenid,account)>PEGS_ACCOUNT_MAX_DEBT) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not possible to take more than " << PEGS_ACCOUNT_MAX_DEBT << "%% of the deposit"); -#else - if (PegsGetRatio(tokenid,account)>100) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not possible to take more than 100%% of the deposit"); -#endif + { + CCerror = strprintf("not possible to take more than %d%% of the deposit",PEGS_ACCOUNT_MAX_DEBT); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); // burn tx does not exist in pegs method but it must be created in order for import validation to pass // fictive burntx input of previous account state tx burntx.vin.push_back(CTxIn(accounttxid,0,CScript())); // fictive output of coins in burn tx - burntx.vout.push_back(MakeBurnOutput(amount,0xffffffff,"PEGSCC",vouts,dummyproof,pegstxid,tokenid,mypk,amount,account,mypk)); + burntx.vout.push_back(MakeBurnOutput(amount,0xffffffff,"PEGSCC",vouts,dummyproof,pegstxid,tokenid,mypk,amount,account)); std::vector leaftxids; BitcoinGetProofMerkleRoot(dummyproof, leaftxids); MerkleBranch newBranch(0, leaftxids); @@ -956,31 +835,29 @@ UniValue PegsGet(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 toke return(retstr); } -UniValue PegsRedeem(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t tokenamount) +UniValue PegsRedeem(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; - CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,pegsfunds=0,funds=0,tokenfunds=0; uint256 accounttxid=zeroid,hashBlock,txid,tmptokenid,oracletxid; + CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,pegsfunds=0,funds=0,tokenfunds=0,amount; uint256 accounttxid=zeroid,hashBlock,txid,tmptokenid,oracletxid; CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0); uint8_t M,N,taddr,prefix,prefix2,wiftype,mypriv[32]; std::vector pubkeys; bool found=false; std::vector bindtxids; cp = CCinit(&C,EVAL_PEGS); cpTokens = CCinit(&CTokens,EVAL_TOKENS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_PEGS]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); pegspk = GetUnspendable(cp,0); - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid. On this chain only valid pegstxid is " << KOMODO_EARLYTXID.GetHex()); if (myGetTransaction(pegstxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find pegstxid " << pegstxid.GetHex()); if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid pegstxid " << pegstxid.GetHex()); for(auto txid : bindtxids) { if (myGetTransaction(txid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find bindtxid " << txid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find bindtxid " << txid.GetHex()); if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid bindtxid " << txid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid bindtxid " << txid.GetHex()); if (tmptokenid==tokenid) { found=true; @@ -988,136 +865,82 @@ UniValue PegsRedeem(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 t } } if (!found) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid tokenid " << tokenid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid tokenid " << tokenid.GetHex()); if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account)==0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cannot find account from which to redeem tokens!"); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cannot find account from which to redeem tokens!"); if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "previous account tx not yet confirmed"); - if (PegsGetRatio(tokenid,account)>=PEGS_ACCOUNT_MAX_DEBT) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cannot redeem when account ratio >= " << PEGS_ACCOUNT_MAX_DEBT << "%%"); - if (tokenamount>account.first-(PegsGetTokensAmountPerPrice(account.second,tokenid)*100/PEGS_ACCOUNT_MAX_DEBT)) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cannot redeem this amount of tokens, you must leave enough tokens to leave account ratio <= " << PEGS_ACCOUNT_MAX_DEBT << "%%"); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "previous account tx not yet confirmed"); LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); - mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); - mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); - if (txfee>0 && (pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))=tokenamount) - { - mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); - mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,mypk,pegspk)); - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokenamount,mypk)); - if (tokenfunds>tokenamount) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_PEGS,tokenfunds-tokenamount,mypk,pegspk)); - if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); - LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); - UniValue retstr = FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodePegsAccountOpRet('R',tokenid,pegstxid,mypk,tokenamount,account,mypk)); - return(retstr); - } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not enough tokens in pegs account (" << tokenfunds << ") to redeem this amount of tokens " << tokenamount); -} - -UniValue PegsClose(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; - CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,pegsfunds=0,funds=0,tokenfunds=0,tokenamount,burnamount; uint256 accounttxid=zeroid,hashBlock,txid,tmptokenid,oracletxid; - CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0); - uint8_t M,N,taddr,prefix,prefix2,wiftype,mypriv[32]; std::vector pubkeys; bool found=false; std::vector bindtxids; - - cp = CCinit(&C,EVAL_PEGS); - cpTokens = CCinit(&CTokens,EVAL_TOKENS); - if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_PEGS]?0:CC_TXFEE; - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - pegspk = GetUnspendable(cp,0); - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid. On this chain only valid pegstxid is " << KOMODO_EARLYTXID.GetHex()); - if (myGetTransaction(pegstxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find pegstxid " << pegstxid.GetHex()); - if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid " << pegstxid.GetHex()); - for(auto txid : bindtxids) - { - if (myGetTransaction(txid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find bindtxid " << txid.GetHex()); - if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid bindtxid " << txid.GetHex()); - if (tmptokenid==tokenid) - { - found=true; - break; - } - } - if (!found) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid tokenid " << tokenid.GetHex()); - if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account)==0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cannot find account to close!"); - if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "previous account tx not yet confirmed"); - LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); - mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); - mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); - Myprivkey(mypriv); - GetCCaddress1of2(cp,coinaddr,mypk,pegspk); - CCaddr1of2set(cp,mypk,pegspk,mypriv,coinaddr); - memset(mypriv,0,32); if ((funds=AddNormalinputs(mtx,mypk,account.second,64,pk.IsValid()))>=account.second ) { - if ((pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) + if (accounttxid!=zeroid && (pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) { pegsfunds+=2*CC_MARKER_VALUE; - tokenamount=account.first; - burnamount=account.second; - if ((tokenfunds=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,mypk,pegspk,tokenamount,64))>=tokenamount) - { - mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); - mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,mypk,pegspk)); - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokenamount,mypk)); - mtx.vout.push_back(CTxOut(account.second,CScript() << ParseHex(HexStr(CCtxidaddr(coinaddr,pegstxid))) << OP_CHECKSIG)); - if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); - account.first=0; - account.second=0; - LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); - UniValue retstr = FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodePegsAccountOpRet('X',tokenid,pegstxid,mypk,burnamount,account,mypk)); - return(retstr); - } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not enough tokens in pegs account (" << tokenfunds << ") to take this amount of tokens " << account.first); + mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); + mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); + Myprivkey(mypriv); + GetCCaddress1of2(cp,coinaddr,mypk,pegspk); + CCaddr1of2set(cp,mypk,pegspk,mypriv,coinaddr); + amount=account.first; + if ((tokenfunds=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,mypk,pegspk,amount,64))>=amount) + { + if (pegsfunds>=txfee+2*CC_MARKER_VALUE) + { + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,mypk,pegspk)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS,amount,mypk)); + mtx.vout.push_back(CTxOut(account.second,CScript() << ParseHex(HexStr(CCtxidaddr(coinaddr,pegstxid))) << OP_CHECKSIG)); + if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); + account.first=0; + account.second=0; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + UniValue retstr = FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodePegsReedemOpRet(tokenid,pegstxid,mypk,amount,account)); + memset(mypriv,0,32); + return(retstr); + } + else + { + memset(mypriv,0,32); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not enough balance in pegs global CC address"); + } + } + memset(mypriv,0,32); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not enough tokens in pegs account (" << tokenfunds << ") to redeem this amount of tokens " << account.first); } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not enough balance in pegs global CC address"); + else + { + memset(mypriv,0,32); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not enough balance in pegs global CC address"); + } } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "to close your account you must return full debt amount " << account.second << " instead of " << funds); + memset(mypriv,0,32); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "to redeem from account and close it you must redeem full debt ammount " << account.second << " instead of " << funds); } + UniValue PegsExchange(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,pegsfunds=0,funds=0,tokenfunds=0,tokenamount,tmpamount; uint256 accounttxid=zeroid,hashBlock,txid,tmptokenid,oracletxid; - CPubKey mypk,pegspk,tmppk,accountpk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0); + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0); uint8_t M,N,taddr,prefix,prefix2,wiftype,mypriv[32]; std::vector pubkeys; bool found=false; std::vector bindtxids; cp = CCinit(&C,EVAL_PEGS); cpTokens = CCinit(&CTokens,EVAL_TOKENS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_PEGS]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); pegspk = GetUnspendable(cp,0); - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid. On this chain only valid pegstxid is " << KOMODO_EARLYTXID.GetHex()); if (myGetTransaction(pegstxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find pegstxid " << pegstxid.GetHex()); if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid pegstxid " << pegstxid.GetHex()); for(auto txid : bindtxids) { if (myGetTransaction(txid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find bindtxid " << txid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find bindtxid " << txid.GetHex()); if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid bindtxid " << txid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid bindtxid " << txid.GetHex()); if (tmptokenid==tokenid) { found=true; @@ -1125,9 +948,9 @@ UniValue PegsExchange(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 } } if (!found) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid tokenid " << tokenid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid tokenid " << tokenid.GetHex()); if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account)!=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "you have active account, please close account first before exchanging other coins!"); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "you have active account, please close account first before exchanging other coins!"); if ((funds=AddNormalinputs(mtx,mypk,amount,64,pk.IsValid()))>=amount ) { if ((pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) @@ -1136,17 +959,17 @@ UniValue PegsExchange(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenfunds=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,pegspk,CPubKey(),tokenamount,64); if (tokenfundsCCpriv,coinaddr); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "previous account tx not yet confirmed"); + tokenfunds+=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,tmppk,pegspk,tokenamount,64); + mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); + mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); + GetCCaddress1of2(cp,coinaddr,tmppk,pegspk); + CCaddr1of2set(cp,tmppk,pegspk,cp->CCpriv,coinaddr); pegsfunds+=2*CC_MARKER_VALUE; } if (tokenfunds>=tokenamount) @@ -1154,13 +977,13 @@ UniValue PegsExchange(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 if (accounttxid!=zeroid) { mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); - mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,accountpk,pegspk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,tmppk,pegspk)); } if ((accounttxid!=zeroid && pegsfunds>=txfee+2*CC_MARKER_VALUE) || pegsfunds>=txfee) { - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokenamount,mypk)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS,tokenamount,mypk)); mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(CCtxidaddr(coinaddr,pegstxid))) << OP_CHECKSIG)); - if (tokenfunds>tokenamount) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_PEGS,tokenfunds-tokenamount,accountpk,pegspk)); + if (tokenfunds>tokenamount) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_PEGS,tokenfunds-tokenamount,tmppk,pegspk)); if (accounttxid!=zeroid) { if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); @@ -1169,43 +992,43 @@ UniValue PegsExchange(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 } else if (pegsfunds>txfee) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-txfee,pegspk)); LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "modified account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodePegsAccountOpRet('E',tokenid,pegstxid,mypk,amount,account,accountpk))); + return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodePegsExchangeOpRet(tokenid,pegstxid,mypk,tmppk,amount,account))); } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not enough balance in pegs global CC address"); + else + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not enough balance in pegs global CC address"); } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not enough tokens in pegs account (" << tokenfunds << ") to exchange to this amount of tokens " << tokenamount); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not enough tokens in pegs account (" << tokenfunds << ") to exchange to this amount of tokens " << tokenamount); } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not enough balance in pegs global CC address"); + else + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not enough balance in pegs global CC address"); } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not enough funds to exchange " << amount << " coins to tokens - balance " << funds); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not enough funds to exchange " << amount << " coins to tokens - balance " << funds); } UniValue PegsLiquidate(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, uint256 liquidatetxid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,pegsfunds=0,funds=0,tokenfunds=0,amount,tmpamount,tokenamount,burnamount; - CPubKey mypk,pegspk,tmppk,accountpk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0),myaccount(0,0); + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0),myaccount(0,0); uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; bool found=false; std::vector bindtxids; uint256 hashBlock,txid,tmptokenid,oracletxid,accounttxid; cp = CCinit(&C,EVAL_PEGS); cpTokens = CCinit(&CTokens,EVAL_TOKENS); if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_PEGS]?0:CC_TXFEE; + txfee = 10000; mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); pegspk = GetUnspendable(cp,0); - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid. On this chain only valid pegstxid is " << KOMODO_EARLYTXID.GetHex()); if (myGetTransaction(pegstxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find pegstxid " << pegstxid.GetHex()); if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid pegstxid " << pegstxid.GetHex()); for(auto txid : bindtxids) { if (myGetTransaction(txid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find bindtxid " << txid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find bindtxid " << txid.GetHex()); if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid bindtxid " << txid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid bindtxid " << txid.GetHex()); if (tmptokenid==tokenid) { found=true; @@ -1213,76 +1036,73 @@ UniValue PegsLiquidate(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint25 } } if (!found) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid tokenid " << tokenid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid tokenid " << tokenid.GetHex()); if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,myaccount)==0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cannot find account, you must have an account to liquidate another account!"); - if (PegsGetRatio(tokenid,myaccount)>=(ASSETCHAINS_PEGSCCPARAMS[0]?ASSETCHAINS_PEGSCCPARAMS[0]:PEGS_ACCOUNT_RED_ZONE)) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not able to liquidate another account when your account ratio is in red zone - ratio > " << (ASSETCHAINS_PEGSCCPARAMS[0]?ASSETCHAINS_PEGSCCPARAMS[0]:PEGS_ACCOUNT_RED_ZONE) << "%%"); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cannot find account, you must have an account to liquidate another account!"); if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "previous account tx not yet confirmed"); - if (liquidatetxid==zeroid || myGetTransaction(liquidatetxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0 || PegsDecodeAccountTx(tx,tmppk,amount,account,accountpk).empty()) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cannot find account to liquidate or invalid tx " << liquidatetxid.GetHex()); - if (PegsGetAccountRatio(pegstxid,tokenid,liquidatetxid)<(ASSETCHAINS_PEGSCCPARAMS[0]?ASSETCHAINS_PEGSCCPARAMS[0]:PEGS_ACCOUNT_RED_ZONE) || PegsGetGlobalRatio(pegstxid)<(ASSETCHAINS_PEGSCCPARAMS[1]?ASSETCHAINS_PEGSCCPARAMS[1]:PEGS_GLOBAL_RED_ZONE)) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not able to liquidate account until account ratio >= " << (ASSETCHAINS_PEGSCCPARAMS[0]?ASSETCHAINS_PEGSCCPARAMS[0]:PEGS_ACCOUNT_RED_ZONE) << "%% and global ratio >= " << (ASSETCHAINS_PEGSCCPARAMS[1]?ASSETCHAINS_PEGSCCPARAMS[1]:PEGS_GLOBAL_RED_ZONE) << "%%"); - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,liquidatetxid,1) != 0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "previous liquidation account tx not yet confirmed"); - LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << myaccount.first << ",debt=" << myaccount.second << "]" << std::endl); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "previous account tx not yet confirmed"); + if (PegsGetAccountRatio(pegstxid,tokenid,liquidatetxid)<(ASSETCHAINS_PEGSCCPARAMS[0]?ASSETCHAINS_PEGSCCPARAMS[0]:PEGS_ACCOUNT_RED_ZONE) || PegsGetGlobalRatio(pegstxid)<(ASSETCHAINS_PEGSCCPARAMS[1]?ASSETCHAINS_PEGSCCPARAMS[1]:PEGS_ACCOUNT_RED_ZONE)) + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not able to liquidate account until account ratio > " << (ASSETCHAINS_PEGSCCPARAMS[0]?ASSETCHAINS_PEGSCCPARAMS[0]:PEGS_ACCOUNT_RED_ZONE) << "% and global ratio > " << (ASSETCHAINS_PEGSCCPARAMS[1]?ASSETCHAINS_PEGSCCPARAMS[1]:PEGS_ACCOUNT_RED_ZONE) << "%"); + if (liquidatetxid!=zeroid && myGetTransaction(liquidatetxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0 || PegsDecodeAccountTx(tx,tmppk,amount,account).empty()) + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cannot find account to liquidate or invalid tx " << liquidatetxid.GetHex()); + if (liquidatetxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,liquidatetxid,1) != 0) + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "previous liquidate account tx not yet confirmed"); + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); tokenamount=account.first; burnamount=account.second; tmpamount=PegsGetTokensAmountPerPrice(burnamount,tokenid)*105/100; amount=tmpamount+((tokenamount-tmpamount)*10/100); - mtx.vin.push_back(CTxIn(liquidatetxid,0,CScript())); - mtx.vin.push_back(CTxIn(liquidatetxid,1,CScript())); if ((funds=AddNormalinputs(mtx,mypk,account.second,64))>=burnamount) { - if ((pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))CCpriv,coinaddr); - if ((tokenfunds=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,accountpk,pegspk,tokenamount,64))==tokenamount) + if (liquidatetxid!=zeroid && (pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) { - if (pegsfunds>=txfee+2*CC_MARKER_VALUE) - { - mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); - mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,accountpk,pegspk)); - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,amount,mypk)); - mtx.vout.push_back(CTxOut(burnamount,CScript() << ParseHex(HexStr(CCtxidaddr(coinaddr,pegstxid))) << OP_CHECKSIG)); - mtx.vout.push_back(MakeTokensCC1vout(EVAL_PEGS,tokenamount-amount,pegspk)); - if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); - account.first=0; - account.second=0; - LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodePegsAccountOpRet('L',tokenid,pegstxid,mypk,burnamount,account,accountpk))); + pegsfunds+=2*CC_MARKER_VALUE; + mtx.vin.push_back(CTxIn(liquidatetxid,0,CScript())); + mtx.vin.push_back(CTxIn(liquidatetxid,1,CScript())); + GetCCaddress1of2(cp,coinaddr,tmppk,pegspk); + CCaddr1of2set(cp,tmppk,pegspk,cp->CCpriv,coinaddr); + if ((tokenfunds=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,tmppk,pegspk,tokenamount,64))==tokenamount) + { + if (pegsfunds>=txfee+2*CC_MARKER_VALUE) + { + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,tmppk,pegspk)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS,amount,mypk)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_PEGS,tokenamount-amount,pegspk)); + mtx.vout.push_back(CTxOut(burnamount,CScript() << ParseHex(HexStr(CCtxidaddr(coinaddr,pegstxid))) << OP_CHECKSIG)); + if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); + account.first=0; + account.second=0; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodePegsLiquidateOpRet(tokenid,pegstxid,mypk,amount,account))); + } + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not enough balance in pegs global CC address"); } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not enough balance in pegs global CC address"); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "tokens amount in pegs account " << tokenfunds << " not matching amount in account " << account.first); // this shouldn't happen } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "tokens amount in pegs account " << tokenfunds << " not matching amount in account " << tokenamount); // this shouldn't happen + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not enough balance in pegs global CC address"); } - else CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "not enough funds to liquidate account, you must liquidate full debt ammount " << txfee+account.second << " instead of " << funds); - + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "not enough funds to liquidate account, you must liquidate full debt ammount " << txfee+account.second << " instead of " << funds); } UniValue PegsAccountHistory(const CPubKey& pk,uint256 pegstxid) { char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmptokenid,tmppegstxid; - CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,tmppk,accountpk; std::map> accounts; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,tmppk; std::map> accounts; std::vector txids; std::pair account; std::vector bindtxids; UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid. On this chain only valid pegstxid is " << KOMODO_EARLYTXID.GetHex()); if (myGetTransaction(pegstxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find pegstxid " << pegstxid.GetHex()); if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid pegstxid " << pegstxid.GetHex()); result.push_back(Pair("result","success")); result.push_back(Pair("name","pegsaccounthistory")); cp = CCinit(&C,EVAL_PEGS); mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); pegspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,coinaddr,mypk,pegspk); - SetCCtxids(txids,coinaddr,true,EVAL_PEGS,CC_MARKER_VALUE,pegstxid,0); + SetCCtxids(txids,coinaddr,true,EVAL_PEGS,pegstxid,0); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { txid = *it; @@ -1290,7 +1110,7 @@ UniValue PegsAccountHistory(const CPubKey& pk,uint256 pegstxid) (funcid=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("action",PegsDecodeAccountTx(tx,tmppk,amount,account,accountpk))); + obj.push_back(Pair("action",PegsDecodeAccountTx(tx,tmppk,amount,account))); obj.push_back(Pair("amount",amount)); obj.push_back(Pair("accounttxid",txid.GetHex())); obj.push_back(Pair("token",PegsGetTokenName(tmptokenid))); @@ -1306,16 +1126,14 @@ UniValue PegsAccountHistory(const CPubKey& pk,uint256 pegstxid) UniValue PegsAccountInfo(const CPubKey& pk,uint256 pegstxid) { char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmptokenid,tmppegstxid; std::map> accounts; - CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,tmppk,accountpk; std::vector bindtxids; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,tmppk; std::vector bindtxids; std::vector > unspentOutputs; std::pair account; UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid. On this chain only valid pegstxid is " << KOMODO_EARLYTXID.GetHex()); if (myGetTransaction(pegstxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find pegstxid " << pegstxid.GetHex()); if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid pegstxid " << pegstxid.GetHex()); result.push_back(Pair("result","success")); result.push_back(Pair("name","pegsaccountinfo")); cp = CCinit(&C,EVAL_PEGS); @@ -1333,7 +1151,7 @@ UniValue PegsAccountInfo(const CPubKey& pk,uint256 pegstxid) (funcid=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid) { //LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "txid=" << txid.GetHex() << ", vout=" << vout << ", nValue=" << nValue << ", tokenid=" << tmptokenid.GetHex() << std::endl); - PegsDecodeAccountTx(tx,tmppk,amount,account,accountpk); + PegsDecodeAccountTx(tx,tmppk,amount,account); accounts[tmptokenid].first=account.first; accounts[tmptokenid].second=account.second; } @@ -1342,7 +1160,6 @@ UniValue PegsAccountInfo(const CPubKey& pk,uint256 pegstxid) { UniValue obj(UniValue::VOBJ); obj.push_back(Pair("token",PegsGetTokenName(it->first))); - obj.push_back(Pair("price",(double)PegsGetTokenPrice(it->first)/COIN)); obj.push_back(Pair("deposit",accounts[it->first].first)); obj.push_back(Pair("debt",accounts[it->first].second)); if (accounts[it->first].first==0 || accounts[it->first].second==0 || PegsGetTokenPrice(it->first)<=0) obj.push_back(Pair("ratio",0)); @@ -1356,16 +1173,14 @@ UniValue PegsAccountInfo(const CPubKey& pk,uint256 pegstxid) UniValue PegsWorstAccounts(uint256 pegstxid) { char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmppegstxid,tokenid,prev; - CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey pegspk,pk,accountpk; double ratio; std::vector bindtxids; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey pegspk,pk; double ratio; std::vector bindtxids; std::vector > unspentOutputs; std::pair account; UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; std::multimap map; - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid. On this chain only valid pegstxid is " << KOMODO_EARLYTXID.GetHex()); if (myGetTransaction(pegstxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find pegstxid " << pegstxid.GetHex()); if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid pegstxid " << pegstxid.GetHex()); result.push_back(Pair("result","success")); result.push_back(Pair("name","pegsworstaccounts")); cp = CCinit(&C,EVAL_PEGS); @@ -1380,7 +1195,7 @@ UniValue PegsWorstAccounts(uint256 pegstxid) if (vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts=tx.vout.size())>0 && (funcid=DecodePegsOpRet(tx,tmppegstxid,tokenid))!=0 && pegstxid==tmppegstxid) { - PegsDecodeAccountTx(tx,pk,amount,account,accountpk); + PegsDecodeAccountTx(tx,pk,amount,account); if (account.first==0 || account.second==0 || PegsGetTokenPrice(tokenid)<=0) ratio=0; else ratio=PegsGetRatio(tokenid,account); if (ratio>PEGS_ACCOUNT_RED_ZONE) @@ -1412,24 +1227,17 @@ UniValue PegsWorstAccounts(uint256 pegstxid) UniValue PegsInfo(uint256 pegstxid) { char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmppegstxid,tokenid; - CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey pegspk,pk,accountpk; std::vector bindtxids; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey pegspk,pk; std::vector bindtxids; std::vector > unspentOutputs; std::pair account; std::map> globalaccounts; double globaldeposit=0; - UniValue result(UniValue::VOBJ),gateways(UniValue::VARR),acc(UniValue::VARR); struct CCcontract_info *cp,C; + UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; - if (KOMODO_EARLYTXID!=zeroid && pegstxid!=KOMODO_EARLYTXID) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid. On this chain only valid pegstxid is " << KOMODO_EARLYTXID.GetHex()); if (myGetTransaction(pegstxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "cant find pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "cant find pegstxid " << pegstxid.GetHex()); if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') - CCERR_RESULT("pegscc",CCLOG_ERROR, stream << "invalid pegstxid " << pegstxid.GetHex()); + CCERR_RESULT("pegscc",CCLOG_INFO, stream << "invalid pegstxid " << pegstxid.GetHex()); result.push_back(Pair("result","success")); result.push_back(Pair("name","pegsinfo")); - for (std::vector::const_iterator it=bindtxids.begin(); it!=bindtxids.end(); it++) - { - gateways.push_back(it->GetHex()); - } - result.push_back(Pair("gateways",gateways)); cp = CCinit(&C,EVAL_PEGS); pegspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,coinaddr,pegspk,pegspk); @@ -1442,7 +1250,7 @@ UniValue PegsInfo(uint256 pegstxid) if (vout == 0 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts=tx.vout.size())>0 && (funcid=DecodePegsOpRet(tx,tmppegstxid,tokenid))!=0 && pegstxid==tmppegstxid) { - PegsDecodeAccountTx(tx,pk,amount,account,accountpk); + PegsDecodeAccountTx(tx,pk,amount,account); globalaccounts[tokenid].first+=account.first; globalaccounts[tokenid].second+=account.second; } @@ -1464,7 +1272,6 @@ UniValue PegsInfo(uint256 pegstxid) { UniValue obj(UniValue::VOBJ); obj.push_back(Pair("token",PegsGetTokenName(it->first))); - obj.push_back(Pair("price",(double)PegsGetTokenPrice(it->first)/COIN)); obj.push_back(Pair("total deposit",globalaccounts[it->first].first)); obj.push_back(Pair("total debt",globalaccounts[it->first].second)); if (globalaccounts[it->first].first==0 || globalaccounts[it->first].second==0 || PegsGetTokenPrice(it->first)<=0) obj.push_back(Pair("total ratio",0)); diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index b7d38372418..0fcc2e512c3 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -74,81 +74,12 @@ GetKomodoEarlytxidScriptPub is on line #2080 of komodo_bitcoind.h #include #include -#include "gmp_i64.h" #define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) #define NVOUT_CCMARKER 1 #define NVOUT_NORMALMARKER 3 -// parse errors -#define PRICESCC_ERR_EXPR_EMPTY (-1) -#define PRICESCC_INCORRECT_OPCODE (-2) -#define PRICESCC_INSUFFICIENT_OPERANDS (-3) -#define PRICESCC_TOO_MANY_OPERANDS (-4) -#define PRICESCC_EXTRA_DATA_IN_EXPR (-5) - -static std::map parse_errors{ - { PRICESCC_ERR_EXPR_EMPTY, "expression is empty" }, - { PRICESCC_INCORRECT_OPCODE, "bad opcode in expression" }, - { PRICESCC_INSUFFICIENT_OPERANDS, "insufficient operands in expression" }, - { PRICESCC_EXTRA_DATA_IN_EXPR, "extra data in expression" } -}; -// expr calc errors -#define PRICESCC_BAD_EXPR_WEIGHT (-2) -#define PRICESCC_BAD_EXPR_MUL (-3) -#define PRICESCC_BAD_EXPR_DIV (-4) -#define PRICESCC_BAD_EXPR_INV (-5) -#define PRICESCC_BAD_EXPR_MDD (-6) -#define PRICESCC_BAD_EXPR_MMD (-7) -#define PRICESCC_BAD_EXPR_MMM (-8) -#define PRICESCC_BAD_EXPR_DDD (-9) -#define PRICESCC_BAD_OPCODE (-10) -#define PRICESCC_EMPTY_TOTAL_WEIGHT (-11) -#define PRICESCC_EXTRA_DATA_IN_STACK (-12) -#define PRICESCC_OVERFLOW (-13) -#define PRICESCC_PRICE_IS_NULL (-14) -#define PRICESCC_ERR_MEMORY (-15) -#define PRICESCC_ERR_CANT_GET_PRICES (-16) - -static std::map calc_errors{ - { PRICESCC_ERR_CANT_GET_PRICES, "could not get prices (end of the chain is possible)" }, - { PRICESCC_BAD_EXPR_WEIGHT, "bad operands for weight opcode" }, - { PRICESCC_BAD_EXPR_MUL, "bad operands for '*' opcode" }, - { PRICESCC_BAD_EXPR_DIV, "bad operands for '/' opcode" }, - { PRICESCC_BAD_EXPR_INV, "bad operands for '!' opcode" }, - { PRICESCC_BAD_EXPR_MDD, "bad operands for '*//' opcode" }, - { PRICESCC_BAD_EXPR_MMD, "bad operands for '**/' opcode" }, - { PRICESCC_BAD_EXPR_MMM, "bad operands for '***' opcode" }, - { PRICESCC_BAD_EXPR_DDD, "bad operands for '///' opcode" }, - { PRICESCC_BAD_OPCODE, "bad opcode" }, - { PRICESCC_EMPTY_TOTAL_WEIGHT, "weight accumulator value empty" }, - { PRICESCC_EXTRA_DATA_IN_STACK, "extra data in expression stack" }, - { PRICESCC_OVERFLOW, "overflow" }, - { PRICESCC_PRICE_IS_NULL, "dto prices value zero (possibly insufficient historical data yet)" } -}; - -// get info errors: -#define PRICESCC_TX_IN_MEMPOOL (-21) -#define PRICESCC_ERR_PARSE_FINAL_TX_OPRET (-22) -#define PRICESCC_ERR_FINAL_TX_STRUCT (-23) -#define PRICESCC_ERR_CANT_LOAD_FINAL_TX (-24) -#define PRICESCC_ERR_EMPTY_BETS (-25) -#define PRICESCC_ERR_SCAN_CHAIN (-26) -#define PRICESCC_ERR_BET_OPRET (-27) -#define PRICESCC_ERR_CANT_GET_BET_TX_OR_BAD_STRUCT (-28) - -static std::map betinfo_errors{ - { PRICESCC_TX_IN_MEMPOOL, "bet transaction in mempool" }, - { PRICESCC_ERR_PARSE_FINAL_TX_OPRET, "could not parse final transaction opreturn" }, - { PRICESCC_ERR_FINAL_TX_STRUCT, "bad final transaction structure" }, - { PRICESCC_ERR_CANT_LOAD_FINAL_TX, "could not load final transaction" }, - { PRICESCC_ERR_EMPTY_BETS, "empty bets" }, - { PRICESCC_ERR_SCAN_CHAIN, "scanning chain error" }, - { PRICESCC_ERR_BET_OPRET, "could not parse bet transaction opreturn" }, - { PRICESCC_ERR_CANT_GET_BET_TX_OR_BAD_STRUCT, "could not load bet transaction or bad transaction structure" } -}; - typedef struct OneBetData { int64_t positionsize; int32_t firstheight; @@ -213,20 +144,7 @@ inline bool is_weight_str(std::string s) { std::count_if(s.begin(), s.end(), [](unsigned char c) { return std::isalpha(c) || c == '/'; } ) == 0; } -// Denormalize previously multiplied price value to move it from uint32_t to UniValue floating point representation -/* not used, used standard ValueFromAmount as all prices values are normalized to 10e8 -UniValue DenormPriceValue(const CAmount& amount, uint32_t mult) -{ - bool sign = amount < 0; - int64_t n_abs = (sign ? -amount : amount); - int64_t quotient = n_abs / mult; - int64_t remainder = n_abs % mult; - int32_t zeros = 0; - while (mult /= 10) zeros++; - return UniValue(UniValue::VNUM, - strprintf("%s%d.%0*d", sign ? "-" : "", quotient, zeros, remainder)); -} -*/ + // start of consensus code CScript prices_betopret(CPubKey mypk,int32_t height,int64_t amount,int16_t leverage,int64_t firstprice,std::vector vec,uint256 tokenid) @@ -567,12 +485,12 @@ bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx if (PricesCheckOpret(vintx, vintxOpret) == 0) { //return eval->Invalid("cannot find prices opret in vintx"); - LOGSTREAMFN("prices", CCLOG_INFO, stream << "cannot find prices opret in vintx" << std::endl); + std::cerr << "PricesValidate() " << "cannot find prices opret in vintx" << std::endl; } if (!IS_CHARINSTR(funcId, "FR") && vintxOpret.begin()[1] == 'B' && prevCCoutN == 1) { //return eval->Invalid("cannot spend bet marker"); - LOGSTREAMFN("prices", CCLOG_INFO, stream << "non-final tx cannot spend cc marker vout=" << prevCCoutN << std::endl); + std::cerr << "PricesValidate() " << " non-final tx cannot spend cc marker vout=" << prevCCoutN << std::endl; } if (!foundFirst) { @@ -589,7 +507,7 @@ bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx if (!IS_CHARINSTR(funcId, "FR") && ccVinCount > 1) {// for all prices tx except final tx only one cc vin is allowed //return eval->Invalid("only one prices cc vin allowed for this tx"); - LOGSTREAMFN("prices", CCLOG_INFO, stream << "only one prices cc vin allowed for this tx" << std::endl); + std::cerr << "PricesValidate() " << "only one prices cc vin allowed for this tx" << std::endl; } switch (funcId) { @@ -599,19 +517,19 @@ bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx case 'A': // add funding // check tx structure: if (!ValidateAddFundingTx(cp, eval, tx, firstVinTx)) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "ValidateAddFundingTx = false " << eval->state.GetRejectReason() << std::endl); + std::cerr << "PricesValidate() " << "ValidateAddFundingTx = false " << eval->state.GetRejectReason() << std::endl; return false; // invalid state is already set in the func } if (firstVinTxOpret.begin()[1] == 'B') { if (!ValidateBetTx(cp, eval, firstVinTx)) {// check tx structure - LOGSTREAMFN("prices", CCLOG_INFO, stream << "funcId=A ValidatebetTx = false " << eval->state.GetRejectReason() << std::endl); + std::cerr << "PricesValidate() " << "funcId=A ValidatebetTx = false " << eval->state.GetRejectReason() << std::endl; return false; // invalid state is already set in the func } } if (prevCCoutN != 0) { // check spending rules - LOGSTREAMFN("prices", CCLOG_INFO, stream << "addfunding tx incorrect vout to spend=" << prevCCoutN << std::endl); + std::cerr << "PricesValidate() " << "addfunding tx incorrect vout to spend=" << prevCCoutN << std::endl; return eval->Invalid("incorrect vintx vout to spend"); } break; @@ -636,15 +554,15 @@ bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx case 'F': // final tx case 'R': if (!ValidateFinalTx(cp, eval, tx, firstVinTx)) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "ValidateFinalTx=false " << eval->state.GetRejectReason() << std::endl); + std::cerr << "PricesValidate() " << "ValidateFinalTx=false " << eval->state.GetRejectReason() << std::endl; return false; } if (!ValidateBetTx(cp, eval, firstVinTx)) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "ValidateBetTx=false " << eval->state.GetRejectReason() << std::endl); + std::cerr << "PricesValidate() " << "ValidateBetTx=false " << eval->state.GetRejectReason() << std::endl; return false; } if (prevCCoutN != 1) { // check spending rules - LOGSTREAMFN("prices", CCLOG_INFO, stream << "final tx incorrect vout to spend=" << prevCCoutN << std::endl); + std::cerr << "PricesValidate() "<< "final tx incorrect vout to spend=" << prevCCoutN << std::endl; return eval->Invalid("incorrect vout to spend"); } break; @@ -734,7 +652,7 @@ static std::string prices_getsourceexpression(const std::vector &vec) for (int32_t i = 0; i < vec.size(); i++) { - char name[PRICES_MAXNAMELENGTH+1]; + char name[65]; std::string operand; uint16_t opcode = vec[i]; int32_t value = (opcode & (KOMODO_MAXPRICES - 1)); // index or weight @@ -823,7 +741,7 @@ static void prices_splitpair(const std::string &pair, std::string &upperquote, s upperquote = pair; bottomquote = ""; } - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "upperquote=" << upperquote << " bottomquote=" << bottomquote << std::endl); + //std::cerr << "prices_splitpair: upperquote=" << upperquote << " bottomquote=" << bottomquote << std::endl; } // invert pair like BTS_USD -> USD_BTC @@ -869,9 +787,9 @@ static void prices_invertoperation(const std::vector &vexpr, int p, } } - // LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "prices_invert inverted="); - // for (auto v : voperation) LOGSTREAM("prices", CCLOG_DEBUG1, stream << v << " "); - // LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << std::endl); + //std::cerr << "prices_invert inverted="; + //for (auto v : voperation) std::cerr << v << " "; + //std::cerr << std::endl; } // reduce pairs in the operation, change or remove opcode if reduced @@ -879,22 +797,23 @@ static int prices_reduceoperands(std::vector &voperation) { int opcount = voperation.size() - 1; int need = opcount; - // LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "begin need=" << need << std::endl); + //std::cerr << "prices_reduceoperands begin need=" << need << std::endl; while (true) { int i; - // LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "opcount=" << opcount << std::endl); + //std::cerr << "prices_reduceoperands opcount=" << opcount << std::endl; for (i = 0; i < opcount; i++) { std::string upperquote, bottomquote; bool breaktostart = false; - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "voperation[i]=" << voperation[i] << " i=" << i << std::endl); + //std::cerr << "prices_reduceoperands voperation[i]=" << voperation[i] << " i=" << i << std::endl; prices_splitpair(voperation[i], upperquote, bottomquote); if (upperquote == bottomquote) { - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "erasing i=" << i << std::endl); + std::cerr << "prices_reduceoperands erasing i=" << i << std::endl; voperation.erase(voperation.begin() + i); opcount--; - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "erased, size=" << voperation.size() << std::endl); + //std::cerr << "prices_reduceoperands erased, size=" << voperation.size() << std::endl; + if (voperation.size() > 0 && voperation.back() == "*") voperation.pop_back(); breaktostart = true; @@ -905,7 +824,8 @@ static int prices_reduceoperands(std::vector &voperation) int j; for (j = i + 1; j < opcount; j++) { - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "voperation[j]=" << voperation[j] << " j=" << j << std::endl); + //std::cerr << "prices_reduceoperands voperation[j]=" << voperation[j] << " j=" << j << std::endl; + std::string upperquotej, bottomquotej; prices_splitpair(voperation[j], upperquotej, bottomquotej); if (upperquote == bottomquotej || bottomquote == upperquotej) { @@ -913,10 +833,10 @@ static int prices_reduceoperands(std::vector &voperation) voperation[i] = upperquotej + "_" + bottomquote; else voperation[i] = upperquote + "_" + bottomquotej; - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "erasing j=" << j << std::endl); + //std::cerr << "prices_reduceoperands erasing j=" << j << std::endl; voperation.erase(voperation.begin() + j); opcount--; - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "erased, size=" << voperation.size() << std::endl); + //std::cerr << "prices_reduceoperands erased, size=" << voperation.size() << std::endl; need--; if (voperation.back() == "***") { @@ -937,7 +857,7 @@ static int prices_reduceoperands(std::vector &voperation) break; } - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "end need=" << need << std::endl); + //std::cerr << "prices_reduceoperands end need=" << need << std::endl; return need; } @@ -980,7 +900,7 @@ static std::string prices_getreducedexpr(const std::string &expr) reduced += vexpr[i]; } - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "reduced=" << reduced << std::endl); + //std::cerr << "reduced=" << reduced << std::endl; return reduced; } @@ -989,8 +909,8 @@ int32_t prices_syntheticvec(std::vector &vec, std::vector { int32_t i, need, ind, depth = 0; std::string opstr; uint16_t opcode, weight; if (synthetic.size() == 0) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "expression is empty" << std::endl); - return(PRICESCC_ERR_EXPR_EMPTY); + std::cerr << "prices_syntheticvec() expression is empty" << std::endl; + return(-1); } for (i = 0; i < synthetic.size(); i++) { @@ -1018,29 +938,29 @@ int32_t prices_syntheticvec(std::vector &vec, std::vector need = 1; } else { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "incorrect opcode=" << opstr << std::endl); - return PRICESCC_INCORRECT_OPCODE; + std::cerr << "prices_syntheticvec() incorrect opcode=" << opstr << std::endl; + return(-2); } if (depth < need) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "incorrect not enough operands for opcode=" << opstr << std::endl); - return PRICESCC_INSUFFICIENT_OPERANDS; + std::cerr << "prices_syntheticvec() incorrect not enough operands for opcode=" << opstr << std::endl; + return(-3); } depth -= need; - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "opcode=" << opcode << " opstr=" << opstr << " need=" << need << " depth=" << depth << std::endl); + ///std::cerr << "prices_syntheticvec() opcode=" << opcode << " opstr=" << opstr << " need=" << need << " depth=" << depth << std::endl; if ((opcode & KOMODO_PRICEMASK) != PRICES_WEIGHT) { // skip weight depth++; // increase operands count - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "depth++=" << depth << std::endl); + ///std::cerr << "depth++=" << depth << std::endl; } if (depth > 3) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "too many operands, last=" << opstr << std::endl); - return PRICESCC_TOO_MANY_OPERANDS; + std::cerr << "prices_syntheticvec() too many operands, last=" << opstr << std::endl; + return(-4); } vec.push_back(opcode); } if (depth != 0) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "depth not empty=" << depth << std::endl); - return PRICESCC_EXTRA_DATA_IN_EXPR; + fprintf(stderr, "prices_syntheticvec() depth.%d not empty\n", depth); + return(-5); } return(0); } @@ -1048,19 +968,11 @@ int32_t prices_syntheticvec(std::vector &vec, std::vector // calculates price for synthetic expression int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t minmax, int16_t leverage) { - int32_t i, int32value, errcode, depth, retval = PRICESCC_ERR_CANT_GET_PRICES; + int32_t i, value, errcode, depth, retval = -1; uint16_t opcode; - - // TODO: maybe to do this variables as mpz too? int64_t *pricedata, pricestack[4], a, b, c; - mpz_t mpzTotalPrice, mpzPriceValue, mpzDen, mpzA, mpzB, mpzC, mpzResult, mpzMAXINT64; - - pricedata = (int64_t *)calloc(sizeof(*pricedata) * 3, 1 + PRICES_DAYWINDOW * 2 + PRICES_SMOOTHWIDTH); - if (pricedata == NULL) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "could not alloc memory" << std::endl); - return PRICESCC_ERR_MEMORY; - } + mpz_t mpzTotalPrice, mpzPriceValue, mpzDen, mpzA, mpzB, mpzC, mpzResult; mpz_init(mpzTotalPrice); mpz_init(mpzPriceValue); @@ -1070,28 +982,27 @@ int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t mpz_init(mpzB); mpz_init(mpzC); mpz_init(mpzResult); - mpz_init(mpzMAXINT64); - mpz_set_ui64(mpzMAXINT64, std::numeric_limits::max()); + pricedata = (int64_t *)calloc(sizeof(*pricedata) * 3, 1 + PRICES_DAYWINDOW * 2 + PRICES_SMOOTHWIDTH); depth = errcode = 0; - mpz_set_si64(mpzTotalPrice, 0); - mpz_set_si64(mpzDen, 0); + mpz_set_si(mpzTotalPrice, 0); + mpz_set_si(mpzDen, 0); for (i = 0; i < vec.size(); i++) { opcode = vec[i]; - int32value = (opcode & (KOMODO_MAXPRICES - 1)); // index or weight + value = (opcode & (KOMODO_MAXPRICES - 1)); // index or weight - mpz_set_ui64(mpzResult, 0); // clear result to test overflow (see below) + mpz_set_ui(mpzResult, 0); // clear result to test overflow (see below) - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "i=" << i << " mpzTotalPrice=" << mpz_get_si64(mpzTotalPrice) << " value=" << value << " depth=" << depth << " opcode&KOMODO_PRICEMASK=" << (opcode & KOMODO_PRICEMASK) < vec, int32_t height, int32_t b = pricestack[--depth]; a = pricestack[--depth]; // pricestack[depth++] = (a * b) / SATOSHIDEN; - mpz_set_si64(mpzA, a); - mpz_set_si64(mpzB, b); + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); mpz_mul(mpzResult, mpzA, mpzB); - mpz_tdiv_q_ui(mpzResult, mpzResult, (uint32_t)SATOSHIDEN); - pricestack[depth++] = mpz_get_si64(mpzResult); // this will be checked for overflow later + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); + pricestack[depth++] = mpz_get_si(mpzResult); + } else - errcode = PRICESCC_BAD_EXPR_MUL; + errcode = -3; break; case PRICES_DIV: // "/" @@ -1149,28 +1061,28 @@ int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t b = pricestack[--depth]; a = pricestack[--depth]; // pricestack[depth++] = (a * SATOSHIDEN) / b; - mpz_set_si64(mpzA, a); - mpz_set_si64(mpzB, b); - mpz_mul_ui(mpzResult, mpzA, (uint32_t)SATOSHIDEN); + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_mul_ui(mpzResult, mpzA, SATOSHIDEN); mpz_tdiv_q(mpzResult, mpzResult, mpzB); - pricestack[depth++] = mpz_get_si64(mpzResult); + pricestack[depth++] = mpz_get_si(mpzResult); } else - errcode = PRICESCC_BAD_EXPR_DIV; + errcode = -4; break; case PRICES_INV: // "!" if (depth >= 1) { a = pricestack[--depth]; // pricestack[depth++] = (SATOSHIDEN * SATOSHIDEN) / a; - mpz_set_si64(mpzA, a); - mpz_set_ui64(mpzResult, (uint32_t)SATOSHIDEN); - mpz_mul_ui(mpzResult, mpzResult, (uint32_t)SATOSHIDEN); + mpz_set_si(mpzA, a); + mpz_set_ui(mpzResult, SATOSHIDEN); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); mpz_tdiv_q(mpzResult, mpzResult, mpzA); - pricestack[depth++] = mpz_get_si64(mpzResult); + pricestack[depth++] = mpz_get_si(mpzResult); } else - errcode = PRICESCC_BAD_EXPR_INV; + errcode = -5; break; case PRICES_MDD: // "*//" @@ -1179,17 +1091,17 @@ int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t b = pricestack[--depth]; a = pricestack[--depth]; // pricestack[depth++] = (((a * SATOSHIDEN) / b) * SATOSHIDEN) / c; - mpz_set_si64(mpzA, a); - mpz_set_si64(mpzB, b); - mpz_set_si64(mpzC, c); - mpz_mul_ui(mpzResult, mpzA, (uint32_t)SATOSHIDEN); + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_mul_ui(mpzResult, mpzA, SATOSHIDEN); mpz_tdiv_q(mpzResult, mpzResult, mpzB); - mpz_mul_ui(mpzResult, mpzResult, (uint32_t)SATOSHIDEN); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); mpz_tdiv_q(mpzResult, mpzResult, mpzC); - pricestack[depth++] = mpz_get_si64(mpzResult); + pricestack[depth++] = mpz_get_si(mpzResult); } else - errcode = PRICESCC_BAD_EXPR_MDD; + errcode = -6; break; case PRICES_MMD: // "**/" @@ -1198,15 +1110,15 @@ int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t b = pricestack[--depth]; a = pricestack[--depth]; // pricestack[depth++] = (a * b) / c; - mpz_set_si64(mpzA, a); - mpz_set_si64(mpzB, b); - mpz_set_si64(mpzC, c); + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); mpz_mul(mpzResult, mpzA, mpzB); mpz_tdiv_q(mpzResult, mpzResult, mpzC); - pricestack[depth++] = mpz_get_si64(mpzResult); + pricestack[depth++] = mpz_get_si(mpzResult); } else - errcode = PRICESCC_BAD_EXPR_MMD; + errcode = -7; break; case PRICES_MMM: // "***" @@ -1215,17 +1127,17 @@ int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t b = pricestack[--depth]; a = pricestack[--depth]; // pricestack[depth++] = (((a * b) / SATOSHIDEN ) * c) / SATOSHIDEN; - mpz_set_si64(mpzA, a); - mpz_set_si64(mpzB, b); - mpz_set_si64(mpzC, c); + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); mpz_mul(mpzResult, mpzA, mpzB); - mpz_tdiv_q_ui(mpzResult, mpzResult, (uint32_t)SATOSHIDEN); + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); mpz_mul(mpzResult, mpzResult, mpzC); - mpz_tdiv_q_ui(mpzResult, mpzResult, (uint32_t)SATOSHIDEN); - pricestack[depth++] = mpz_get_si64(mpzResult); + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); + pricestack[depth++] = mpz_get_si(mpzResult); } else - errcode = PRICESCC_BAD_EXPR_MMM; + errcode = -8; break; case PRICES_DDD: // "///" @@ -1234,31 +1146,31 @@ int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t b = pricestack[--depth]; a = pricestack[--depth]; //pricestack[depth++] = (((((SATOSHIDEN * SATOSHIDEN) / a) * SATOSHIDEN) / b) * SATOSHIDEN) / c; - mpz_set_si64(mpzA, a); - mpz_set_si64(mpzB, b); - mpz_set_si64(mpzC, c); - mpz_set_ui64(mpzResult, (uint32_t)SATOSHIDEN); - mpz_mul_ui(mpzResult, mpzResult, (uint32_t)SATOSHIDEN); + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_set_ui(mpzResult, SATOSHIDEN); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); mpz_tdiv_q(mpzResult, mpzResult, mpzA); - mpz_mul_ui(mpzResult, mpzResult, (uint32_t)SATOSHIDEN); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); mpz_tdiv_q(mpzResult, mpzResult, mpzB); - mpz_mul_ui(mpzResult, mpzResult, (uint32_t)SATOSHIDEN); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); mpz_tdiv_q(mpzResult, mpzResult, mpzC); - pricestack[depth++] = mpz_get_si64(mpzResult); + pricestack[depth++] = mpz_get_si(mpzResult); } else - errcode = PRICESCC_BAD_EXPR_DDD; + errcode = -9; break; default: - errcode = PRICESCC_BAD_OPCODE; + errcode = -10; break; } - // LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "test mpzResult=" << mpz_get_si64(mpzResult) << std::endl); + // std::cerr << "prices_syntheticprice test mpzResult=" << mpz_get_si(mpzResult) << std::endl; // check overflow: - if (mpz_cmp(mpzResult, mpzMAXINT64) > 0) { - errcode = PRICESCC_OVERFLOW; + if (mpz_cmp_si(mpzResult, std::numeric_limits::max()) > 0) { + errcode = -13; break; } @@ -1266,58 +1178,56 @@ int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t break; // if( depth > 0 ) - // LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "top pricestack[depth-1=" << depth-1 << "]=" << pricestack[depth-1] << std::endl); + // std::cerr << "prices_syntheticprice top pricestack[depth-1=" << depth-1 << "]=" << pricestack[depth-1] << std::endl; // else - // LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "pricestack empty" << std::endl); + // std::cerr << "prices_syntheticprice pricestack empty" << std::endl; } free(pricedata); - - mpz_clear(mpzMAXINT64); mpz_clear(mpzResult); mpz_clear(mpzA); mpz_clear(mpzB); mpz_clear(mpzC); - if( mpz_get_si64(mpzDen) != 0 ) + if( mpz_get_si(mpzDen) != 0 ) mpz_tdiv_q(mpzTotalPrice, mpzTotalPrice, mpzDen); // price / den - int64_t den = mpz_get_si64(mpzDen); - int64_t priceIndex = mpz_get_si64(mpzTotalPrice); + int64_t den = mpz_get_si(mpzDen); + int64_t priceIndex = mpz_get_si(mpzTotalPrice); mpz_clear(mpzDen); mpz_clear(mpzTotalPrice); mpz_clear(mpzPriceValue); if (errcode != 0) - LOGSTREAMFN("prices", CCLOG_ERROR, stream << "errcode in switch=" << errcode << std::endl); + std::cerr << "prices_syntheticprice errcode in switch=" << errcode << std::endl; - if( errcode == PRICESCC_ERR_CANT_GET_PRICES ) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "error getting price (could be end of chain)" << std::endl); + if( errcode == -1 ) { + std::cerr << "prices_syntheticprice error getting price (could be end of chain)" << std::endl; return errcode; } - if (errcode == PRICESCC_OVERFLOW) { - LOGSTREAMFN("prices", CCLOG_ERROR, stream << "overflow in price" << std::endl); + if (errcode == -13) { + std::cerr << "prices_syntheticprice overflow in price" << std::endl; return errcode; } - if (errcode == PRICESCC_PRICE_IS_NULL) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "price is zero, not enough historical data yet or end of chain reached" << std::endl); + if (errcode == -14) { + std::cerr << "prices_syntheticprice price is zero, not enough historic data yet" << std::endl; return errcode; } if (den == 0) { - LOGSTREAMFN("prices", CCLOG_ERROR, stream << "den==0 in expr" << std::endl); - return PRICESCC_EMPTY_TOTAL_WEIGHT; + std::cerr << "prices_syntheticprice den==0 return err=-11" << std::endl; + return(-11); } else if (depth != 0) { - LOGSTREAMFN("prices", CCLOG_ERROR, stream << "depth!=0 in expr" << std::endl); - return PRICESCC_EXTRA_DATA_IN_STACK; + std::cerr << "prices_syntheticprice depth!=0 err=-12" << std::endl; + return(-12); } else if (errcode != 0) { - LOGSTREAMFN("prices", CCLOG_ERROR, stream << "unknown err=" << errcode << std::endl); + std::cerr << "prices_syntheticprice err=" << errcode << std::endl; return(errcode); } - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "priceIndex=totalprice/den=" << priceIndex << " den=" << den << std::endl); +// std::cerr << "prices_syntheticprice priceIndex=totalprice/den=" << priceIndex << " den=" << den << std::endl; return priceIndex; } @@ -1326,10 +1236,15 @@ int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t height, int16_t leverage, std::vector vec, int64_t positionsize, int64_t &profits, int64_t &outprice) { int64_t price; +#ifndef TESTMODE const int32_t COSTBASIS_PERIOD = PRICES_DAYWINDOW; +#else + const int32_t COSTBASIS_PERIOD = 7; +#endif + if (height < firstheight) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "requested height is lower than bet firstheight=" << height << std::endl); + fprintf(stderr, "requested height is lower than bet firstheight.%d\n", height); return -1; } @@ -1337,7 +1252,7 @@ int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t if ((price = prices_syntheticprice(vec, height, minmax, leverage)) < 0) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "error getting synthetic price at height=" << height << std::endl); + fprintf(stderr, "error getting synthetic price at height.%d\n", height); return -1; } @@ -1353,10 +1268,10 @@ int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t } else if (leverage < 0 && (costbasis == 0 || price < costbasis)) { costbasis = price; - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "minmax costbasis=" << costbasis << std::endl); + //std::cerr << "prices_syntheticprofits() minmax costbasis=" << costbasis << std::endl; } //else { //-> use the previous value - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "unchanged costbasis=" << costbasis << " price=" << price << " leverage=" << leverage << std::endl); + // std::cerr << "prices_syntheticprofits() unchanged costbasis=" << costbasis << " price=" << price << " leverage=" << leverage << std::endl; //} } else { @@ -1365,7 +1280,7 @@ int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t //costbasis = price; // use calculated minmax costbasis - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "use permanent costbasis=" << costbasis << " at height=" << height << std::endl); + //std::cerr << "prices_syntheticprofits() use permanent costbasis=" << costbasis << " at height=" << height << std::endl; //} } @@ -1394,30 +1309,26 @@ int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t mpz_t mpzCostbasis; mpz_t mpzPrice; mpz_t mpzLeverage; - mpz_t mpzPositionsize; mpz_init(mpzProfits); mpz_init(mpzCostbasis); mpz_init(mpzPrice); mpz_init(mpzLeverage); - mpz_init(mpzPositionsize); - mpz_set_si64(mpzCostbasis, costbasis); - mpz_set_si64(mpzPrice, price); - mpz_mul_ui(mpzPrice, mpzPrice, (uint32_t)SATOSHIDEN); // (price*SATOSHIDEN) + mpz_set_si(mpzCostbasis, costbasis); + mpz_set_si(mpzPrice, price); + mpz_mul_ui(mpzPrice, mpzPrice, SATOSHIDEN); // (price*SATOSHIDEN) mpz_tdiv_q(mpzProfits, mpzPrice, mpzCostbasis); // profits = (price*SATOSHIDEN)/costbasis // normalization - mpz_sub_ui(mpzProfits, mpzProfits, (uint32_t)SATOSHIDEN); // profits -= SATOSHIDEN + mpz_sub_ui(mpzProfits, mpzProfits, SATOSHIDEN); // profits -= SATOSHIDEN - mpz_set_si64(mpzLeverage, leverage); + mpz_set_si(mpzLeverage, leverage); mpz_mul(mpzProfits, mpzProfits, mpzLeverage); // profits *= leverage - mpz_set_si64(mpzPositionsize, positionsize); - mpz_mul(mpzProfits, mpzProfits, mpzPositionsize); // profits *= positionsize - mpz_tdiv_q_ui(mpzProfits, mpzProfits, (uint32_t)SATOSHIDEN); // profits /= SATOSHIDEN // de-normalization + mpz_mul_ui(mpzProfits, mpzProfits, positionsize); // profits *= positionsize + mpz_tdiv_q_ui(mpzProfits, mpzProfits, SATOSHIDEN); // profits /= SATOSHIDEN // de-normalization - profits = mpz_get_si64(mpzProfits); + profits = mpz_get_si(mpzProfits); - mpz_clear(mpzPositionsize); mpz_clear(mpzLeverage); mpz_clear(mpzProfits); mpz_clear(mpzCostbasis); @@ -1527,10 +1438,10 @@ int64_t prices_enumaddedbets(uint256 &batontxid, std::vector &bets, added.positionsize = amount; added.firstheight = blockIdx.GetHeight(); //TODO: check if this is correct (to get height from the block not from the opret) bets.push_back(added); - //LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "added amount=" << amount << std::endl); + //std::cerr << "prices_batontxid() added amount=" << amount << std::endl; } else { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "cannot load or decode add bet tx, isLoaded=" << isLoaded << " funcId=" << (int)funcId << std::endl); + std::cerr << "prices_batontxid() cannot load or decode add bet tx, isLoaded=" << isLoaded << " funcId=" << (int)funcId << std::endl; return -1; } sourcetxid = batontxid; @@ -1547,7 +1458,6 @@ UniValue PricesBet(int64_t txfee, int64_t amount, int16_t leverage, std::vector< struct CCcontract_info *cp, C; CPubKey pricespk, mypk; int64_t betamount, firstprice = 0; - int32_t parse_err; std::vector vec; //char myaddr[64]; std::string rawtx; @@ -1564,17 +1474,10 @@ UniValue PricesBet(int64_t txfee, int64_t amount, int16_t leverage, std::vector< mypk = pubkey2pk(Mypubkey()); pricespk = GetUnspendable(cp, 0); //GetCCaddress(cp, myaddr, mypk); - if ((parse_err = prices_syntheticvec(vec, synthetic)) < 0 || (firstprice = prices_syntheticprice(vec, nextheight - 1, 1, leverage)) < 0 || vec.size() == 0 || vec.size() > 4096) + if (prices_syntheticvec(vec, synthetic) < 0 || (firstprice = prices_syntheticprice(vec, nextheight - 1, 1, leverage)) < 0 || vec.size() == 0 || vec.size() > 4096) { result.push_back(Pair("result", "error")); - if (parse_err < 0) - result.push_back(Pair("error", "invalid synthetic: " + parse_errors[parse_err])); - else if (firstprice < 0) - result.push_back(Pair("error", "invalid synthetic: " + calc_errors[firstprice])); - else if (vec.size() == 0) - result.push_back(Pair("error", "invalid synthetic: " + std::string("parsed vector is empty"))); - else - result.push_back(Pair("error", "invalid synthetic: " + std::string("expr too long"))); + result.push_back(Pair("error", "invalid synthetic")); return(result); } @@ -1608,7 +1511,7 @@ UniValue PricesBet(int64_t txfee, int64_t amount, int16_t leverage, std::vector< GetKomodoEarlytxidScriptPub(); } mtx.vout.push_back(CTxOut(amount-betamount, KOMODO_EARLYTXID_SCRIPTPUB)); - // test: mtx.vout.push_back(CTxOut(amount - betamount, CScript() << ParseHex("037c803ec82d12da939ac04379bbc1130a9065c53d8244a61eece1db942cf0efa7") << OP_CHECKSIG)); // vout4 test revshare fee + //test: mtx.vout.push_back(CTxOut(amount - betamount, CScript() << ParseHex("037c803ec82d12da939ac04379bbc1130a9065c53d8244a61eece1db942cf0efa7") << OP_CHECKSIG)); // vout4 test revshare fee rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_betopret(mypk, nextheight - 1, amount, leverage, firstprice, vec, zeroid)); return(prices_rawtxresult(result, rawtx, 0)); @@ -1699,6 +1602,9 @@ UniValue PricesAddFunding(int64_t txfee, uint256 bettxid, int64_t amount) // scan chain from the initial bet's first position upto the chain tip and calculate bet's costbasises and profits, breaks if rekt detected int32_t prices_scanchain(std::vector &bets, int16_t leverage, std::vector vec, int64_t &lastprice, int32_t &endheight) { + if (bets.size() == 0) + return -1; + bool stop = false; for (int32_t height = bets[0].firstheight+1; ; height++) // the last datum for 24h is the costbasis value { @@ -1712,7 +1618,7 @@ int32_t prices_scanchain(std::vector &bets, int16_t leverage, std::v int32_t retcode = prices_syntheticprofits(bets[i].costbasis, bets[i].firstheight, height, leverage, vec, bets[i].positionsize, bets[i].profits, lastprice); if (retcode < 0) { - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "function prices_syntheticprofits returned -1, finishing..." << std::endl); + std::cerr << "prices_scanchain() prices_syntheticprofits returned -1, finishing..." << std::endl; stop = true; break; } @@ -1843,7 +1749,7 @@ int32_t prices_getbetinfo(uint256 bettxid, BetInfo &betinfo) if (myGetTransaction(bettxid, bettx, hashBlock) && bettx.vout.size() > 3) { if (hashBlock.IsNull()) - return PRICESCC_TX_IN_MEMPOOL; + return -2; // TODO: forget old tx @@ -1876,30 +1782,22 @@ int32_t prices_getbetinfo(uint256 bettxid, BetInfo &betinfo) CTransaction finaltx; uint256 hashBlock; vscript_t vopret; - if (myGetTransaction(finaltxid, finaltx, hashBlock)) - { - if (finaltx.vout.size() > 0 && PricesCheckOpret(finaltx, vopret) != 0) - { - uint8_t funcId = prices_finalopretdecode(finaltx.vout.back().scriptPubKey, betinfo.txid, betinfo.pk, betinfo.lastheight, betinfo.averageCostbasis, betinfo.lastprice, betinfo.liquidationprice, betinfo.equity, betinfo.exitfee); - if (funcId == 0) - return PRICESCC_ERR_PARSE_FINAL_TX_OPRET; + if (myGetTransaction(finaltxid, finaltx, hashBlock) && finaltx.vout.size() > 0 && PricesCheckOpret(finaltx, vopret) != 0) { + uint8_t funcId = prices_finalopretdecode(finaltx.vout.back().scriptPubKey, betinfo.txid, betinfo.pk, betinfo.lastheight, betinfo.averageCostbasis, betinfo.lastprice, betinfo.liquidationprice, betinfo.equity, betinfo.exitfee); + if (funcId == 0) + return -3; - betinfo.isRekt = (funcId == 'R'); - } - else - return PRICESCC_ERR_FINAL_TX_STRUCT; + betinfo.isRekt = (funcId == 'R'); // return 0; } else - return PRICESCC_ERR_CANT_LOAD_FINAL_TX; + return -6; } - if (betinfo.bets.size() == 0) - return PRICESCC_ERR_EMPTY_BETS; if (prices_scanchain(betinfo.bets, betinfo.leverage, betinfo.vecparsed, betinfo.lastprice, betinfo.lastheight) < 0) { - return PRICESCC_ERR_SCAN_CHAIN; + return -4; } mpz_t mpzTotalPosition; @@ -1916,24 +1814,25 @@ int32_t prices_getbetinfo(uint256 bettxid, BetInfo &betinfo) for (auto b : betinfo.bets) { mpz_t mpzProduct; mpz_t mpzProfits; - mpz_t mpzPositionsize; mpz_init(mpzProduct); mpz_init(mpzProfits); - mpz_init(mpzPositionsize); - mpz_set_ui64(mpzProduct, b.costbasis); - mpz_set_ui64(mpzPositionsize, b.positionsize); - mpz_mul(mpzProduct, mpzProduct, mpzPositionsize); // b.costbasis * b.amount + //totalprofits += b.profits; + //dcostbasis += b.amount * (double)b.costbasis; + // costbasis += b.amount * (b.costbasis / PRICES_POINTFACTOR); // prevent int64 overflow (but we have underflow for 1/BTC) + // std::cerr << "PricesInfo() acc dcostbasis=" << dcostbasis << " b.amount=" << b.amount << " b.costbasis/PRICES_POINTFACTOR=" << (b.costbasis / PRICES_POINTFACTOR) << std::endl; + //std::cerr << "PricesInfo() acc dcostbasis=" << dcostbasis << " b.amount=" << b.amount << " b.costbasis/PRICES_POINTFACTOR=" << (b.costbasis / PRICES_POINTFACTOR) << std::endl; + mpz_set_ui(mpzProduct, b.costbasis); + mpz_mul_ui(mpzProduct, mpzProduct, (uint64_t)b.positionsize); // b.costbasis * b.amount mpz_add(mpzTotalcostbasis, mpzTotalcostbasis, mpzProduct); //averageCostbasis += b.costbasis * b.amount; - mpz_add(mpzTotalPosition, mpzTotalPosition, mpzPositionsize); //totalposition += b.amount; + mpz_add_ui(mpzTotalPosition, mpzTotalPosition, (uint64_t)b.positionsize); //totalposition += b.amount; mpz_add(mpzTotalprofits, mpzTotalprofits, mpzProfits); //totalprofits += b.profits; totalposition += b.positionsize; totalprofits += b.profits; - mpz_clear(mpzPositionsize); mpz_clear(mpzProduct); mpz_clear(mpzProfits); } @@ -1941,17 +1840,17 @@ int32_t prices_getbetinfo(uint256 bettxid, BetInfo &betinfo) betinfo.equity = totalposition + totalprofits; //int64_t averageCostbasis = 0; - if (mpz_get_ui64(mpzTotalPosition) != 0) { //prevent zero div + if (mpz_get_ui(mpzTotalPosition) != 0) { //prevent zero div mpz_t mpzAverageCostbasis; mpz_init(mpzAverageCostbasis); //averageCostbasis = totalcostbasis / totalposition; - mpz_mul_ui(mpzTotalcostbasis, mpzTotalcostbasis, (uint32_t)SATOSHIDEN); // profits *= SATOSHIDEN normalization to prevent loss of significance while division + mpz_mul_ui(mpzTotalcostbasis, mpzTotalcostbasis, SATOSHIDEN); // profits *= SATOSHIDEN normalization to prevent loss of significance while division mpz_tdiv_q(mpzAverageCostbasis, mpzTotalcostbasis, mpzTotalPosition); - mpz_tdiv_q_ui(mpzAverageCostbasis, mpzAverageCostbasis, (uint32_t)SATOSHIDEN); // profits /= SATOSHIDEN de-normalization + mpz_tdiv_q_ui(mpzAverageCostbasis, mpzAverageCostbasis, SATOSHIDEN); // profits /= SATOSHIDEN de-normalization - betinfo.averageCostbasis = mpz_get_ui64(mpzAverageCostbasis); + betinfo.averageCostbasis = mpz_get_ui(mpzAverageCostbasis); mpz_clear(mpzAverageCostbasis); } @@ -1976,9 +1875,9 @@ int32_t prices_getbetinfo(uint256 bettxid, BetInfo &betinfo) mpz_clear(mpzTotalcostbasis); return 0; } - return PRICESCC_ERR_BET_OPRET; + return -3; } - return PRICESCC_ERR_CANT_GET_BET_TX_OR_BAD_STRUCT; + return (-1); } // pricesrekt rpc: anyone can rekt a bet at some block where losses reached limit, collecting fee @@ -2002,10 +1901,24 @@ UniValue PricesRekt(int64_t txfee, uint256 bettxid, int32_t rektheight) BetInfo betinfo; int32_t retcode = prices_getbetinfo(bettxid, betinfo); - if (retcode < 0) - { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", betinfo_errors[retcode])); + if (retcode < 0) { + if (retcode == -1) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); + } + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); + return(result); + } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); + } return(result); } @@ -2102,10 +2015,24 @@ UniValue PricesCashout(int64_t txfee, uint256 bettxid) BetInfo betinfo; int32_t retcode = prices_getbetinfo(bettxid, betinfo); - if (retcode < 0) - { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", betinfo_errors[retcode])); + if (retcode < 0) { + if (retcode == -1) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); + } + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); + return(result); + } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); + } return(result); } @@ -2161,10 +2088,28 @@ UniValue PricesInfo(uint256 bettxid, int32_t refheight) BetInfo betinfo; int32_t retcode = prices_getbetinfo(bettxid, betinfo); - if (retcode < 0) - { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", betinfo_errors[retcode])); + if (retcode < 0) { + if( retcode == -1 ) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); + } + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); + return(result); + } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); + } + else { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", retcode)); + } return(result); } @@ -2184,7 +2129,9 @@ UniValue PricesInfo(uint256 bettxid, int32_t refheight) result.push_back(Pair("reduced", prices_getreducedexpr(expr))); // result.push_back(Pair("batontxid", batontxid.GetHex())); result.push_back(Pair("costbasis", ValueFromAmount(betinfo.averageCostbasis))); - result.push_back(Pair("costbasis_period", PRICES_DAYWINDOW)); +#ifdef TESTMODE + result.push_back(Pair("costbasis_test_period", 7)); +#endif prices_betjson(result, betinfo.bets, betinfo.leverage, betinfo.lastheight, betinfo.lastprice); @@ -2241,7 +2188,7 @@ UniValue PricesList(uint32_t filter, CPubKey mypk) if (bAppend) result.push_back(txid.GetHex()); } - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "bettxid=" << txid.GetHex() << " mypk=" << HexStr(mypk) << " opretpk=" << HexStr(pk) << " filter=" << filter << " bAppend=" << bAppend << std::endl); + std::cerr << "PricesList() " << " bettxid=" << txid.GetHex() << " mypk=" << HexStr(mypk) << " opretpk=" << HexStr(pk) << " filter=" << filter << " bAppend=" << bAppend << std::endl; } }; @@ -2282,7 +2229,7 @@ static bool prices_ispositionup(const std::vector &vecparsed, int16_t int32_t value = (opcode & (KOMODO_MAXPRICES - 1)); // filter index or weight = opcode & (2048-1) if ((opcode & KOMODO_PRICEMASK) == 0) { - char name[PRICES_MAXNAMELENGTH + 1]; + char name[65]; if (komodo_pricename(name, value)) { std::string upperquote, bottomquote; prices_splitpair(std::string(name), upperquote, bottomquote); @@ -2290,16 +2237,16 @@ static bool prices_ispositionup(const std::vector &vecparsed, int16_t uint16_t opcode1 = vecparsed[1]; bool isInverted = ((opcode1 & KOMODO_PRICEMASK) == PRICES_INV); - // LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "upperquote=" << upperquote << " bottomquote=" << bottomquote << " opcode1=" << opcode1 << " (opcode1 & KOMODO_PRICEMASK)=" << (opcode1 & KOMODO_PRICEMASK) << std::endl); + //std::cerr << "prices_ispositionup upperquote=" << upperquote << " bottomquote=" << bottomquote << " opcode1=" << opcode1 << " (opcode1 & KOMODO_PRICEMASK)=" << (opcode1 & KOMODO_PRICEMASK) << std::endl; if (upperquote == "BTC" || bottomquote == "BTC") { // it is relatively btc if (upperquote == "BTC" && (leverage > 0 && !isInverted || leverage < 0 && isInverted) || bottomquote == "BTC" && (leverage < 0 && !isInverted || leverage > 0 && isInverted)) { - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "returns true for BTC for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl); + std::cerr << "prices_ispositionup returns true for BTC for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl; return true; } else { - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "returns false for BTC for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl); + std::cerr << "prices_ispositionup returns false for BTC for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl; return false; } } @@ -2307,18 +2254,18 @@ static bool prices_ispositionup(const std::vector &vecparsed, int16_t if (upperquote == "USD" || bottomquote == "USD") { // it is relatively usd if (upperquote == "USD" && (leverage > 0 && !isInverted || leverage < 0 && isInverted) || bottomquote == "USD" && (leverage < 0 && !isInverted || leverage > 0 && isInverted)) { - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "returns true for USD for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl); + std::cerr << "prices_ispositionup returns true for USD for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl; return true; } else { - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "returns false for USD for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl); + std::cerr << "prices_ispositionup returns false for USD for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl; return false; } } } } } - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "returns false for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl); + std::cerr << "prices_ispositionup returns false for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl; return false; } @@ -2332,8 +2279,8 @@ static bool prices_isopposite(BetInfo p1, BetInfo p2) { int32_t value2 = (opcode2 & (KOMODO_MAXPRICES - 1)); // index or weight if ( (opcode1 & KOMODO_PRICEMASK) == 0 && (opcode2 & KOMODO_PRICEMASK) == 0 ) { - char name1[PRICES_MAXNAMELENGTH + 1]; - char name2[PRICES_MAXNAMELENGTH + 1]; + char name1[65]; + char name2[65]; if (komodo_pricename(name1, value1) && komodo_pricename(name2, value2)) { uint16_t opcode1 = p1.vecparsed[1]; @@ -2365,7 +2312,7 @@ static std::string findMatchedBook(const std::vector &vecparsed, const int32_t value = (opcode & (KOMODO_MAXPRICES - 1)); // filter index or weight = opcode & (2048-1) if ((opcode & KOMODO_PRICEMASK) == 0) { - char name[PRICES_MAXNAMELENGTH + 1]; + char name[65]; if (komodo_pricename(name, value)) { auto it = bookmatched.find(std::string(name)); if (it != bookmatched.end()) @@ -2420,7 +2367,7 @@ void prices_getorderbook(std::map > & bookmatc fundTotals.totalEquity += book[0].equity; if (book[0].vecparsed.size() <= 3) { // only short expr check for match: "BTC_USD,1" or "BTC_USD,!,1" - char name[PRICES_MAXNAMELENGTH + 1]; + char name[65]; komodo_pricename(name, (book[0].vecparsed[0] & (KOMODO_MAXPRICES - 1))); std::string sname = name; bookmatched[sname].push_back(book[0]); @@ -2481,19 +2428,19 @@ static bool prices_isacceptableamount(const std::vector &vecparsed, in prices_getorderbook(matchedBook, matchedTotals, fundTotals); std::string pricename = findMatchedBook(vecparsed, matchedBook); if (!pricename.empty()) { - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "found matched book=" << pricename << " diffLeveragedPosition=" << matchedTotals[pricename].diffLeveragedPosition << " expr=" << prices_getsourceexpression(vecparsed) << std::endl); + std::cerr << "prices_isacceptableamount() found matched book=" << pricename << " diffLeveragedPosition=" << matchedTotals[pricename].diffLeveragedPosition << " expr=" << prices_getsourceexpression(vecparsed) << std::endl; // could fit into leveraged amount if (prices_ispositionup(vecparsed, leverage) && amount*abs(leverage) + matchedTotals[pricename].diffLeveragedPosition <= 0) { - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "could fit into opposite negative lev amount" << std::endl); + std::cerr << "prices_isacceptableamount() could fit into opposite negative lev amount" << std::endl; return true; } if (!prices_ispositionup(vecparsed, leverage) && -amount*abs(leverage) + matchedTotals[pricename].diffLeveragedPosition >= 0) { - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "could fit into opposite positive lev amount" << std::endl); + std::cerr << "prices_isacceptableamount() could fit into opposite positive lev amount" << std::endl; return true; } } - LOGSTREAMFN("prices", CCLOG_DEBUG1, stream << "amount=" << amount << " leverage=" << leverage << " fundTotals.totalFund=" << fundTotals.totalFund << " fundTotals.totalEquity=" << fundTotals.totalEquity << std::endl); + std::cerr << "prices_isacceptableamount() amount=" << amount << " leverage=" << leverage << " fundTotals.totalFund=" << fundTotals.totalFund << " fundTotals.totalEquity=" << fundTotals.totalEquity << std::endl; // if not fit to matched = allow to bet for leveraged amount no more than 10% from free fund if (amount * leverage < (fundTotals.totalFund - fundTotals.totalEquity) * PRICES_MINAVAILFUNDFRACTION) return true; @@ -2516,8 +2463,7 @@ UniValue PricesGetOrderbook() prices_getorderbook(matchedBook, matchedTotals, fundTotals); - /* not used: - UniValue resbook (UniValue::VARR); + /*UniValue resbook (UniValue::VARR); for (int i = 0; i < book.size(); i++) { UniValue entry(UniValue::VOBJ); entry.push_back(Pair("expression", prices_getsourceexpression(book[i].vecparsed))); diff --git a/src/cc/pricesfeed.cpp b/src/cc/pricesfeed.cpp deleted file mode 100644 index 139e1f10646..00000000000 --- a/src/cc/pricesfeed.cpp +++ /dev/null @@ -1,1006 +0,0 @@ -/****************************************************************************** -* Copyright © 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ - -#include -#include -#include -#include -#include - -#include -#include -#include -#ifndef _WIN32 -#include -#else -#include -#include -#endif - -#include "CCPrices.h" -#include "pricesfeed.h" -#include "priceslibs/priceslibs.h" -#include "priceslibs/cjsonpointer.h" - -#ifdef LOGSTREAM -#undef LOGSTREAM -#endif - -#ifdef LOGSTREAMFN -#undef LOGSTREAMFN -#endif - -template -static void logJsonPath(const char *fname, T errToStream) { - std::ostringstream stream; - errToStream(stream); - if (fname) - std::cerr << fname << " "; - std::cerr << stream.str(); -} -// use redefined log functions because some code in this source is called in komodo init when 'debug is not parsed yet, -// so the output of some log functions in CCinclude may be lost -#define LOGSTREAM(name, level, streamexp) logJsonPath(NULL, [=](std::ostringstream &stream){ streamexp; }) -#define LOGSTREAMFN(name, level, streamexp) logJsonPath(__func__, [=](std::ostringstream &stream){ streamexp; }) - -// external defs: -cJSON *get_urljson(char *url); - -// load so libs helpers: -static void *my_so_open(const char *unixpath) -{ -#ifndef _WIN32 - std::string ospath(unixpath); - ospath += ".so"; - void * plib = dlopen(ospath.c_str(), RTLD_LAZY); -#else - std::string ospath; - const char *p = unixpath; - char fullpath[MAX_PATH] = ""; - while (*p) - ospath += (*p == '/') ? '\\' : *p, p++; - ospath += ".dll"; //LoadLibraryA adds .dll itself - GetFullPathNameA(ospath.c_str(), sizeof(fullpath), fullpath, NULL); - void * plib = (void*)::LoadLibraryA(fullpath); - unsigned e = GetLastError(); - std::cerr << __func__ << " fullpath=" << fullpath << " error=" << e << std::endl; -#endif - return plib; -} - -static void *my_so_get_sym(void *handle, const char *procname) -{ -#ifndef _WIN32 - void * sym = dlsym(handle, procname); -#else - void * sym = (void*)::GetProcAddress((HMODULE)handle, procname); -#endif - return sym; -} - -static void my_so_close(void *handle) -{ -#ifndef _WIN32 - dlclose(handle); -#else - ::FreeLibrary((HMODULE)handle); -#endif -} - - -typedef struct _PriceStatus { - std::string symbol; - uint32_t averageValue; // polled value, average value if the symbol is polled several urls - uint32_t multiplier; - std::set feedConfigIds; // if the same symbol is polled from several web sources, this is the indexes of feedConfig which did the update -} PriceStatus; - -static std::vector pricesStatuses; - -static std::vector feedconfig({ - { - // default feed: - "basic", // name - "", // custom lib.so - "http://api.coindesk.com/v1/bpi/currentprice.json", // url - {}, // substitutes - "", // quote - { }, // substituteResult - { // manyResults: - { "BTC_USD", "/bpi/USD/rate_float" }, // symbol and valuepath - { "BTC_GBP", "/bpi/GBP/rate_float" }, - { "BTC_EUR", "/bpi/EUR/rate_float" } - }, - PF_DEFAULTINTERVAL, // interval - 10000 // multiplier - } -}); - -struct CPollStatus -{ - time_t lasttime; - void *customlibHandle; - CustomJsonParser customJsonParser; - CustomClamper customClamper; - CustomValidator customValidator; - CustomConverter customConverter; - - CPollStatus() { - lasttime = 0L; - customlibHandle = NULL; - customJsonParser = NULL; - customClamper = NULL; - customValidator = NULL; - customConverter = NULL; - } - ~CPollStatus() { - if (customlibHandle) - my_so_close(customlibHandle); - } -}; - -static std::vector pollStatuses; - -// check if string is a float -static bool is_string_float(const std::string &s) -{ - const char *p = s.c_str(); - int count = 0; - while (*p && (*p == '+' || *p == '-' || *p == '.' || isdigit(*p))) p++, count++; - return (count > 0 && count == s.length()); -} - -// init vector with prices symbols and values -bool init_prices_statuses() -{ - int32_t nsymbols = 0, nduplicates = 0; - pricesStatuses.reserve(128); - - pricesStatuses.push_back({ "", 0, 0 }); // first item is timestamp - - for (const auto &citem : feedconfig) - { - auto addName = [&](const std::string &name)->bool { - std::vector::iterator iter = std::find_if(pricesStatuses.begin(), pricesStatuses.end(), [&](const PriceStatus &p) { return p.symbol == name;}); - if (iter == pricesStatuses.end()) { - pricesStatuses.push_back({ name, 0, citem.multiplier }); - nsymbols++; - } - else - { - if (iter->multiplier != citem.multiplier) { - LOGSTREAM("prices", CCLOG_INFO, stream << "init_prices_statuses cannot initialize prices, different multipliers are set for a symbol=" << name << std::endl); - return false; - } - nduplicates++; - } - return true; - }; - - if (!citem.substitutes.empty()) { - for (const auto &s : citem.substitutes) { - std::string name = s; - if (!citem.quote.empty()) - name += "_" + citem.quote; - if (!addName(name)) - return false; - } - } - else { - for (const auto &r : citem.manyResults) - if (!addName(r.symbol)) - return false; - - } - } - LOGSTREAMFN("prices", CCLOG_INFO, stream << "initialized symbols=" << nsymbols << " duplicates=" << nduplicates << std::endl); - return true; -} - -bool init_poll_statuses() -{ - int itemcount = feedconfig.size(); - while (itemcount--) - pollStatuses.push_back(CPollStatus()); - - for (int i = 0; i < feedconfig.size(); i ++) - { - if (!feedconfig[i].customlib.empty()) - { - std::string libpath = "./cc/priceslibs/" + feedconfig[i].customlib; - pollStatuses[i].customlibHandle = my_so_open(libpath.c_str()); - if (pollStatuses[i].customlibHandle == NULL) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "ERROR: can't load prices custom lib=" << libpath << std::endl); - return false; - } - pollStatuses[i].customJsonParser = (CustomJsonParser)my_so_get_sym(pollStatuses[i].customlibHandle, PF_CUSTOM_PARSER_FUNCNAME); - if (pollStatuses[i].customJsonParser == NULL) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "ERROR: can't load custom json parser function=" << PF_CUSTOM_PARSER_FUNCNAME << " from custom lib=" << feedconfig[i].customlib << std::endl); - return false; - } - - pollStatuses[i].customClamper = (CustomClamper)my_so_get_sym(pollStatuses[i].customlibHandle, PF_CUSTOM_CLAMPER_FUNCNAME); - if (pollStatuses[i].customClamper == NULL) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "can't load custom clamper function=" << PF_CUSTOM_CLAMPER_FUNCNAME << " from custom lib=" << feedconfig[i].customlib << std::endl); - // no return false, maybe omitted - } - pollStatuses[i].customValidator = (CustomValidator)my_so_get_sym(pollStatuses[i].customlibHandle, PF_CUSTOM_VALIDATOR_FUNCNAME); - if (pollStatuses[i].customValidator == NULL) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "can't load custom validator function=" << PF_CUSTOM_VALIDATOR_FUNCNAME << " from custom lib=" << feedconfig[i].customlib << std::endl); - // omitting allowed, no return false; - } - - pollStatuses[i].customConverter = (CustomConverter)my_so_get_sym(pollStatuses[i].customlibHandle, PF_CUSTOM_CONVERTER_FUNCNAME); - if (pollStatuses[i].customConverter == NULL) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "can't load custom converter function=" << PF_CUSTOM_CONVERTER_FUNCNAME << " from custom lib=" << feedconfig[i].customlib << std::endl); - // no return false, maybe omitted - } - } - } - return true; -} -// parse poll configuration in json from cmd line -bool PricesFeedParseConfig(const cJSON *json) -{ - if (!cJSON_IsArray(json)) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config not a json array" << std::endl); - return false; - } - - for (int i = 0; i < cJSON_GetArraySize(json); i++) - { - cJSON *jitem = cJSON_GetArrayItem(json, i); - if (!cJSON_IsObject(jitem)) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config element not a object" << std::endl); - return false; - } - CFeedConfigItem citem; - if (cJSON_HasObjectItem(jitem, "name")) - citem.name = cJSON_GetObjectItem(jitem, "name")->valuestring; - if (citem.name.empty()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item has no 'name'" << std::endl); - return false; - } - - if (cJSON_HasObjectItem(jitem, "customlib")) - citem.customlib = cJSON_GetObjectItem(jitem, "customlib")->valuestring; - - if (cJSON_HasObjectItem(jitem, "url")) - citem.url = cJSON_GetObjectItem(jitem, "url")->valuestring; - - if (citem.url.empty()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "prices feed config item has no 'url'" << std::endl); - return false; - } - if (cJSON_HasObjectItem(jitem, "substitutes")) { - cJSON *jsubst = cJSON_GetObjectItem(jitem, "substitutes"); - if (!cJSON_IsArray(jsubst)) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item 'substitutes' is not an array" << std::endl); - return false; - } - for (int j = 0; j < cJSON_GetArraySize(jsubst); j++) - { - cJSON *jsubstitem = cJSON_GetArrayItem(jsubst, j); - std::string subst = jsubstitem->valuestring; - if (subst.empty()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item 'substitutes' is empty" << std::endl); - return false; - } - citem.substitutes.push_back(subst); - } - } - - // check we have substitute macro in url: - if (citem.substitutes.size() > 0 && citem.url.find("%s") == std::string::npos) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item 'url' has no '%s' macro required for 'substitutes'" << std::endl); - return false; - } - - if (cJSON_HasObjectItem(jitem, "results")) - { - auto parseResults = [](const cJSON *jres) -> CFeedConfigItem::ResultProcessor { - std::string symbol, valuepath, customdata; - std::vector averagepaths; - - //const cJSON *jsymbolpath = cJSON_GetObjectItem(jres, "symbolpath"); // not supported for substitutes - //if (jsymbolpath) - // citem.substituteResult.symbolpath = jsymbolpath->valuestring; - - const cJSON *jsymbol = cJSON_GetObjectItem(jres, "symbol"); - if (jsymbol) - symbol = jsymbol->valuestring; - - const cJSON *jvaluepath = cJSON_GetObjectItem(jres, "valuepath"); - if (jvaluepath) - valuepath = jvaluepath->valuestring; - - const cJSON *javeragepaths = cJSON_GetObjectItem(jres, "averagevaluepaths"); - if (javeragepaths) { - if (cJSON_IsArray(javeragepaths)) { - for (int i = 0; i < cJSON_GetArraySize(javeragepaths); i++) { - const cJSON *jitem = cJSON_GetArrayItem(javeragepaths, i); - if (cJSON_IsString(jitem)) { - std::string sitem = jitem->valuestring; - averagepaths.push_back(sitem); - } - } - } - } - const cJSON *jcustomdata = cJSON_GetObjectItem(jres, "customdata"); - if (jcustomdata) - customdata = jcustomdata->valuestring; - - return { symbol, valuepath, averagepaths, customdata }; - }; - - const cJSON *jres = cJSON_GetObjectItem(jitem, "results"); - if (cJSON_IsObject(jres)) - { - // for substitutes single results object is used - citem.substituteResult = parseResults(jres); - - if (citem.customlib.empty() && citem.substituteResult.valuepath.empty() && citem.substituteResult.averagepaths.empty()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item has no correct 'results' object: no 'valuepath' or 'averagevaluepaths' elements" << std::endl); - return false; - } - if (!citem.substituteResult.valuepath.empty() && !citem.substituteResult.averagepaths.empty()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item has no correct 'results' object: can't specify both 'valuepath' and 'averagevaluepaths'" << std::endl); - return false; - } - } - else if (cJSON_IsArray(jres)) - { - // if no substitutes, it should be an array of results - for (int j = 0; j < cJSON_GetArraySize(jres); j++) - { - cJSON *jresitem = cJSON_GetArrayItem(jres, j); - if (cJSON_IsObject(jresitem)) - { - CFeedConfigItem::ResultProcessor res = parseResults(jresitem); - - if (citem.customlib.empty() && res.symbol.empty()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item has no correct 'results' array: no 'symbol' element" << std::endl); - return false; - } - - if (citem.customlib.empty() && res.valuepath.empty() && res.averagepaths.empty()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item has no correct 'results' array: no either 'valuepath' or 'averagevaluepaths' elements" << std::endl); - return false; - } - if (!res.valuepath.empty() && !res.averagepaths.empty()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item has no correct 'results' object: can't specify both 'valuepath' and 'averagevaluepaths'" << std::endl); - return false; - } - citem.manyResults.push_back(res); - } - else - { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item 'results' array element has incorrect type" << std::endl); - return false; - } - } - } - else { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item has no correct 'results' element, should be either object or array" << std::endl); - return false; - } - } - - // check config rules for results - if (citem.customlib.empty()) { - if (!cJSON_HasObjectItem(jitem, "results")) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item has no 'results' element" << std::endl); - return false; - } - if (citem.substitutes.size() == 0 && citem.manyResults.empty()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item has no correct 'results' element: not an array or empty" << std::endl); - return false; - } - } - - // quote which is added to symbol to complete it like: for "quote":"BTC" and "symbol":"USD" extened symbol is "USD_BTC" - if (cJSON_HasObjectItem(jitem, "quote")) - { - citem.quote = cJSON_GetObjectItem(jitem, "quote")->valuestring; - if (citem.quote.empty()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item 'quote' is incorrect" << std::endl); - return false; - } - if (!citem.manyResults.empty()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "use of 'quote' is allowed only for 'substitutes' configuration" << std::endl); - return false; - } - } - - // multiplier to convert price value to uint32 - citem.multiplier = 1; // default value - cJSON *jmultiplier = cJSON_GetObjectItem(jitem, "multiplier"); - if (jmultiplier) { - if (cJSON_IsNumber(jmultiplier) && jmultiplier->valuedouble >= 1) - citem.multiplier = jmultiplier->valuedouble; - else { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item 'multiplier' value is incorrect, should be number >= 1" << std::endl); - return false; - } - } - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item 'multiplier' used value=" << citem.multiplier << std::endl); - - citem.interval = PF_DEFAULTINTERVAL; //default value currently is 120 sec - cJSON *jinterval = cJSON_GetObjectItem(jitem, "interval"); - if (jinterval) { - if (cJSON_IsNumber(jinterval) && jinterval->valuedouble >= PF_MININTERVAL) - citem.interval = jinterval->valuedouble; - else { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item 'interval' value is incorrect, should be number >= " << PF_MININTERVAL << std::endl); - return false; - } - } - LOGSTREAMFN("prices", CCLOG_INFO, stream << "config item 'interval' used value=" << citem.interval << std::endl); - - feedconfig.push_back(citem); // add new feed config item - } - - return true; -} - - -bool PricesInitStatuses() -{ - // get symbols and init prices status for each symbol: - if (!init_prices_statuses()) - return false; - - // init statuses for the configured feeds: - if (!init_poll_statuses()) - return false; - - if (PricesFeedSymbolsCount() > KOMODO_MAXPRICES - 1) { - LOGSTREAMFN("prices", CCLOG_ERROR, stream << "prices values too big: " << PricesFeedSymbolsCount() << std::endl); - return false; - } - return true; -} - -// compatibility with default forex quotes -void PricesAddOldForexConfig(const std::vector &ac_forex) -{ - CFeedConfigItem citem = { - // default feed: - "forex", // name - "", // no custom lib.so - "https://api.openrates.io/latest?base=USD", // url - { }, // empty - "", // empty quote - { }, // substituteResult not used - {}, // manyResults - PF_DEFAULTINTERVAL, // interval - 10000 // multiplier - }; - - for (const auto & name : ac_forex) - { - // add result processor item - CFeedConfigItem::ResultProcessor resItem; - resItem.symbol = std::string("USD_") + name; - resItem.valuepath = std::string("/rates/") + name; - citem.manyResults.push_back(resItem); - } - feedconfig.push_back(citem); // add new feed config item -} - -// compatibility with ac_prices param -void PricesAddOldPricesConfig(const std::vector &ac_prices) -{ - CFeedConfigItem citem = { - // default feed: - "prices", // name - "", // no custom lib.so - "https://api.binance.com/api/v1/ticker/price?symbol=%sBTC", // url - { "KMD", "ETH" }, // default substitutes KMD and ETH - "BTC", // quote - { "", "/price" }, // substituteResult - { }, // manyResults not used - PF_DEFAULTINTERVAL, // interval - 100000000 // multiplier - }; - - for (const auto & name : ac_prices) - { - citem.substitutes.push_back(name); - } - feedconfig.push_back(citem); // add new feed config item -} - -// compatibility with ac_stocks param -void PricesAddOldStocksConfig(const std::vector &ac_stocks) -{ - CFeedConfigItem citem = { - // default feed: - "stocks", // name - "", // no custom lib.so - "https://api.iextrading.com/1.0/tops/last?symbols=", // url, symbols to be added yet - {}, // base substitutes - "", // quote - {}, // substituteResult not used - {}, // manyResults - PF_DEFAULTINTERVAL, // interval - 100 // multiplier - }; - - for (int i = 0; i < ac_stocks.size(); i ++) - { - citem.url += ac_stocks[i]; - if (i < ac_stocks.size() - 1) - citem.url += ","; - - // add result processor item - CFeedConfigItem::ResultProcessor resItem; - resItem.symbol = ac_stocks[i] + std::string("_USD"); - resItem.valuepath = std::string("/") + std::to_string(i) + std::string("/price"); - citem.manyResults.push_back(resItem); - } - feedconfig.push_back(citem); // add new feed config item -} - - -// return number of a configured feed's symbols to get -static uint32_t feed_config_size(const CFeedConfigItem &citem) -{ - if (!citem.substitutes.empty()) - return citem.substitutes.size(); - else - return citem.manyResults.size(); -} - -// return price name for index -char *PricesFeedSymbolName(char *name, int32_t ind) -{ - if (ind >= 1 && ind < pricesStatuses.size()) { - if (strlen(pricesStatuses[ind].symbol.c_str()) < PRICES_MAXNAMELENGTH-1) - strcpy(name, pricesStatuses[ind].symbol.c_str()); - else - { - strncpy(name, pricesStatuses[ind].symbol.c_str(), PRICES_MAXNAMELENGTH-1); - name[PRICES_MAXNAMELENGTH-1] = '\0'; - } - return name; - } - return NULL; -} - -// get config item multiplier -// ind begins from 1 -int64_t PricesFeedMultiplier(int32_t ind) -{ - if (ind == 0) - return 1; //dummy value for 'timestamp' element, not used really - - int32_t offset = 1; - for (const auto & citem : feedconfig) { - uint32_t size1 = feed_config_size(citem); - if (ind - offset < size1) - return citem.multiplier; - offset += size1; - } - return 0; -} - -// returns how many names added to pricesNames (names could be added in random order) -int32_t PricesFeedSymbolsCount() -{ - return pricesStatuses.size(); -} - -// returns string with all price names parameters (for including into the chain magic) -void PricesFeedSymbolsForMagic(std::string &names, bool compatible) -{ - names.reserve(PricesFeedSymbolsCount() * 4); // reserve space considering that mean value is somewhere between BTS_USD, AAMTS, XAU,... - - for (const auto &ci : feedconfig) - { - if (!compatible || (ci.name == "prices" || ci.name == "stocks")) // exclude others for compatibility with old version magic - { - if (!ci.substitutes.empty()) { - // make names from substitutes: - for (const auto &s : ci.substitutes) { - std::string name = s; - // TODO: removed for compat with prev version: - if (!compatible && !ci.quote.empty()) - name += "_" + ci.quote; - if (compatible) { - size_t pos_ = name.find('_'); - if (pos_ != std::string::npos) // remove _USD for compatibility - name = name.substr(0, pos_); - } - if (!compatible || (name != "KMD" && name != "ETH")) // exclude for compatibility - names += name; - } - } - else { - // make names from manyResults symbols : - for (const auto &r : ci.manyResults) { - std::string name = r.symbol; - if (compatible) { - size_t pos_ = name.find('_'); - if (pos_ != std::string::npos) // remove _USD for compatibility - name = name.substr(0, pos_); - } - if (!compatible || (name != "KMD" && name != "ETH")) - names += name; - } - } - } - } - LOGSTREAMFN("prices", CCLOG_INFO, stream << " feed magic names=" << names << std::endl); -} - -void PricesFeedGetCustomProcessors(std::vector &priceProcessors) -{ - int32_t offset = 1; - for (int32_t i = 0; i < feedconfig.size() && i < pollStatuses.size(); i ++) - { - CCustomProcessor p; - - p.b = offset; - p.e = offset + feed_config_size(feedconfig[i]); - p.parser = pollStatuses[i].customJsonParser; - p.clamper = pollStatuses[i].customClamper; - p.validator = pollStatuses[i].customValidator; - p.converter = pollStatuses[i].customConverter; - offset = p.e; - priceProcessors.push_back(p); - } -} - - -// extract price value (and symbol name if required) -// note: extracting symbol names from json is disabled, probably we won't ever need this -// to enable this we should switch to delayed initialization of pricesStatuses (until all symbols will be extracted from all the feeds) -static bool parse_result_json_value(const cJSON *json, /*const std::string &symbolpath,*/ const std::string &valuepath, uint32_t multiplier, /*std::string &symbol,*/ uint32_t *pricevalue) -{ - std::string error; - const cJSON *jvalue = SimpleJsonPointer(json, valuepath.c_str(), error); - if (jvalue) - { - // reliable processing of value: allow either number or string - if (cJSON_IsNumber(jvalue)) { - *pricevalue = jvalue->valuedouble * multiplier; - } - else if (cJSON_IsString(jvalue) && is_string_float(jvalue->valuestring)) { - *pricevalue = atof(jvalue->valuestring) * multiplier; - } - else - { - *pricevalue = 0; - char *sjson = cJSON_Print(json); - LOGSTREAMFN("prices", CCLOG_INFO, stream << "feed json value not a number, path=" << valuepath << " json=" << sjson << std::endl); - if (sjson) - cJSON_free(sjson); - return false; - } - } - else - { - *pricevalue = 0; - LOGSTREAMFN("prices", CCLOG_INFO, stream << "feed json value not found" << std::endl); - return false; - } - -/* it is not supported getting symbols from the json as we need to know them on the config stage - if (!symbolpath.empty()) - { - const cJSON *jsymbol = SimpleJsonPointer(json, symbolpath.c_str()); - if (jsymbol) - { - if (cJSON_IsString(jsymbol)) { - symbol = jsymbol->valuestring; - } - else - { - symbol = ""; - LOGSTREAMFN("prices", CCLOG_INFO, stream << "feed json symbol not a string" << std::endl); - return false; - } - } - else - { - symbol = ""; - LOGSTREAMFN("prices", CCLOG_INFO, stream << "feed json symbol not found" << std::endl); - return false; - } - } -*/ - return true; -} - - -// calc average value by enumerating value arrays by json paths with "*" as array indexes -static bool parse_result_json_average(const cJSON *json, const std::vector &valuepaths, uint32_t multiplier, uint32_t *pricevalue) -{ - double total = 0.0; - int32_t count = 0; - - for (const auto &origpath : valuepaths) - { - std::function enumJsonOnLevel = [&](const cJSON *json, const std::string &path)->void - { - if (!json) return; - - size_t starpos = path.find('*'); - if (starpos != std::string::npos) - { - // found "*" wildcard symbol - std::string toppath = path.substr(0, starpos); - std::string restpath = path.substr(starpos+1); - - int ind = 0; - while (1) { - std::string jerror; - std::string toppathind = toppath + std::to_string(ind++); - const cJSON *jfound = SimpleJsonPointer(json, toppathind.c_str(), jerror); - - // note that names are added on komodo init when -debug has not been parsed yet and LOGSTREAM output won't be shown at this stage - // so we use redefined LOGSTREAM (not that one in CCinclude.h) which sends always to std::cerr - //LOGSTREAM("prices", CCLOG_DEBUG2, stream << "enumJsonOnLevel searching index subpath=" << toppathind << " " << (jfound ? "found" : "null") << std::endl); - if (!jfound) - break; - if (restpath.empty()) - { - if (cJSON_IsNumber(jfound)) { - total += jfound->valuedouble; - count++; - } - else if (cJSON_IsString(jfound) && is_string_float(jfound->valuestring)) { // allow price value as string - total += atof(jfound->valuestring); - count++; - } - else - LOGSTREAM("prices", CCLOG_DEBUG2, stream << "enumJsonOnLevel array leaf value not a number" << std::endl); - - } - else - enumJsonOnLevel(jfound, restpath); // object or array - } - } - else - { - std::string jerror; - // should be leaf value - const cJSON *jfound = SimpleJsonPointer(json, path.c_str(), jerror); - //LOGSTREAM("prices", CCLOG_DEBUG2, stream << "enumJsonOnLevel checking last subpath=" << path << " " << (jfound ? "found" : "null") << std::endl); - if (jfound) { - if (cJSON_IsNumber(jfound)) { - total += jfound->valuedouble; - count++; - } - else - LOGSTREAM("prices", CCLOG_DEBUG2, stream << "enumJsonOnLevel object leaf value not a number" << std::endl); - } - else - LOGSTREAM("prices", CCLOG_DEBUG2, stream << "enumJsonOnLevel leaf not found: " << jerror << std::endl); - } - }; - enumJsonOnLevel(json, origpath); - } - - if (count > 0) { - *pricevalue = (uint32_t) (total / count * multiplier); - return true; - } - else { - *pricevalue = 0; - return false; - } -} - -// pool single url using substitutes or without them -static uint32_t poll_one_feed(const CFeedConfigItem &citem, const CPollStatus &pollStat, uint32_t *pricevalues, std::vector &symbols) -{ - uint32_t numadded = 0; - - LOGSTREAMFN("prices", CCLOG_INFO, stream << "polling..." << std::endl); - if (citem.substitutes.size() > 0) - { - // substitute %s in url, call url and get the symbol from json result - for (const auto subst : citem.substitutes) - { - size_t mpos = citem.url.find("%s"); - if (mpos != std::string::npos) - { - std::string url = citem.url; - url.replace(mpos, 2, subst); - cJSON *json = get_urljson((char*)url.c_str()); //poll - if (json != NULL) - { - std::string symbol; - //std::string jsymbol; - bool parsed; - //if (citem.substituteResult.symbolpath.empty()) { - symbol = subst; - if (!citem.quote.empty()) - symbol += "_" + citem.quote; - //} - //else - // symbol = jsymbol; - - if (!citem.customlib.empty()) { - if (pollStat.customJsonParser) { - char *sjson = cJSON_Print(json); - parsed = pollStat.customJsonParser(sjson, symbol.c_str(), citem.substituteResult.customdata.c_str(), citem.multiplier, &pricevalues[numadded++]); - if (sjson) - cJSON_free(sjson); - } - } - else if (!citem.substituteResult.averagepaths.empty()) { - parsed = parse_result_json_average(json, citem.substituteResult.averagepaths, citem.multiplier, &pricevalues[numadded++]); - if (!parsed) - LOGSTREAM("prices", CCLOG_ERROR, stream << "error parse symbol=" + symbol << std::endl); - } - else { - parsed = parse_result_json_value(json, /*citem.substituteResult.symbolpath,*/ citem.substituteResult.valuepath, citem.multiplier, /*jsymbol,*/ &pricevalues[numadded++]); - if (!parsed) - LOGSTREAM("prices", CCLOG_ERROR, stream << "error parse symbol=" + symbol << " valuepath=" << citem.substituteResult.valuepath << std::endl); - } - if (parsed) { - symbols.push_back(symbol); - LOGSTREAM("prices", CCLOG_INFO, stream << symbol << " " << pricevalues[numadded - 1] << " "); - } - cJSON_Delete(json); - if (!parsed) - return 0; - } - else - { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "feed service not available: " << url << std::endl); - return 0; - } - // pause to prevent ban by web resource - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - } - } - else - { // many values in one json result - cJSON *json = get_urljson((char*)citem.url.c_str()); // poll - if (json != NULL) - { - bool parsed = false; - const std::string empty; - std::string dummy; - - for (const auto &r : citem.manyResults) - { - if (!citem.customlib.empty()) { - if (pollStat.customJsonParser) { - char *sjson = cJSON_Print(json); - parsed = pollStat.customJsonParser(sjson, r.symbol.c_str(), r.customdata.c_str(), citem.multiplier, &pricevalues[numadded++]); - if (sjson) - cJSON_free(sjson); - } - } - else if (!r.averagepaths.empty()) { - parsed = parse_result_json_average(json, r.averagepaths, citem.multiplier, &pricevalues[numadded++]); - if (!parsed) - LOGSTREAM("prices", CCLOG_ERROR, stream << "error parse symbol=" + r.symbol << std::endl); - } - else { - parsed = parse_result_json_value(json, /*empty,*/ r.valuepath, citem.multiplier, /*dummy,*/ &pricevalues[numadded++]); - if (!parsed) - LOGSTREAM("prices", CCLOG_ERROR, stream << "error parse symbol=" + r.symbol << " valuepath=" << r.valuepath << std::endl); - } - if (parsed) - symbols.push_back(r.symbol); - else - break; - - LOGSTREAM("prices", CCLOG_INFO, stream << r.symbol << " " << pricevalues[numadded-1] << " "); - } - cJSON_Delete(json); - if (!parsed) - return 0; - } - else - { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "feed service not available: " << citem.url << std::endl); - return 0; - } - // pause to prevent ban by the web resource - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - LOGSTREAM("prices", CCLOG_INFO, stream << std::endl); - return numadded; -} - -void store_price_value(const std::string &symbol, int32_t configid, uint32_t value) -{ - std::vector::iterator iter = std::find_if(pricesStatuses.begin(), pricesStatuses.end(), [&](const PriceStatus &p) { return p.symbol == symbol;}); - if (iter == pricesStatuses.end()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "internal error: can't store value, no such symbol: " << symbol << std::endl); - return; - } - - // std::cerr << __func__ << "\t" << "before feedConfigIds.size()=" << iter->feedConfigIds.size() << " averagevalue=" << iter->averageValue << " configid=" << configid << std::endl; - if (iter->feedConfigIds.find(configid) != iter->feedConfigIds.end()) - { - // if configid repeats, this means a new poll cycle begins then reset average value and clear configids: - iter->averageValue = value; - iter->feedConfigIds.clear(); - iter->feedConfigIds.insert(configid); - } - else - { - // recalculate average value and store additional polling configid - iter->averageValue = (uint32_t)((uint64_t)iter->averageValue * iter->feedConfigIds.size() + value) / (iter->feedConfigIds.size() + 1); - iter->feedConfigIds.insert(configid); - } - // std::cerr << __func__ << "\t" << "after feedConfigIds.size()=" << iter->feedConfigIds.size() << " averagevalue=" << iter->averageValue << std::endl; -} - -uint32_t PricesFeedPoll(uint32_t *pricevalues, const uint32_t maxsize, uint32_t *timestamp) -{ - uint32_t offset; - uint32_t currentValNumber = 0; - uint32_t nsymbols = 0; - time_t now = time(NULL); - *timestamp = (uint32_t)now; - bool updated = false; - - // dont do this, should be old values! - // memset(pricevalues, '\0', maxsize); // reset to 0 as some feeds maybe updated, some not in this poll - - pricevalues[0] = *timestamp; //set timestamp - offset = 1; // one off - - for (int32_t iconfig = 0; iconfig < feedconfig.size(); iconfig++) - { - uint32_t size1 = feed_config_size(feedconfig[iconfig]); - std::shared_ptr pricesbuf1((uint32_t*)calloc(size1, sizeof(uint32_t))); - - if (!pollStatuses[iconfig].lasttime || now > pollStatuses[iconfig].lasttime + feedconfig[iconfig].interval) // first time poll - { - //LOGSTREAMFN("prices", CCLOG_INFO, stream << "entering poll, !pollStatuses[iconfig].lasttime=" << !pollStatuses[iconfig].lasttime << std::endl); - std::vector symbols; - // poll url and get values and symbols - if (poll_one_feed(feedconfig[iconfig], pollStatuses[iconfig], pricesbuf1.get(), symbols) > 0) - { - if (size1 != symbols.size()) { - LOGSTREAMFN("prices", CCLOG_INFO, stream << "internal error: incorrect returned symbol size" << std::endl); - return 0; - } - for (int32_t ibuf = 0; ibuf < size1; ibuf++) - { - store_price_value(symbols[ibuf], iconfig, pricesbuf1.get()[ibuf]); - } - updated = true; - pollStatuses[iconfig].lasttime = now; // TODO: may we need to get new time here as could be delays in polls? - } - } - - // free(pricesbuf1); use shared_ptr - - if (offset + size1 >= maxsize) - return PF_BUFOVERFLOW; // buffer overflow - - offset += size1; - } - if (updated) { - // unload price values to the output buffer - for (int i = 1; i < pricesStatuses.size(); i++) - pricevalues[i] = pricesStatuses[i].averageValue; // one off - return pricesStatuses.size(); - } - else - return 0; // no update in this poll -} diff --git a/src/cc/pricesfeed.h b/src/cc/pricesfeed.h deleted file mode 100644 index f7663e7e30e..00000000000 --- a/src/cc/pricesfeed.h +++ /dev/null @@ -1,81 +0,0 @@ -/****************************************************************************** -* Copyright © 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ - -#ifndef __PRICES_FEED__ -#define __PRICES_FEED__ - -#include -#include -#include -#include "priceslibs/priceslibs.h" - -#include - -// support for creating data feeds for prices cc - -#define PF_BUFOVERFLOW 0xFFFFFFFF -#define PF_DEFAULTINTERVAL 120 -#define PF_MININTERVAL 60 - -struct CFeedConfigItem { - - std::string name; // config name - std::string customlib; // custom shared lib - std::string url; // url, can contain '%s' where substitute strings ill be places - std::vector substitutes; // array of strings that will be substituted in the 'url' to form the request - std::string quote; - - struct ResultProcessor - { - // std::string symbolpath; // not supported by now - std::string symbol; // symbol names - std::string valuepath; // json pointers to values - std::vector averagepaths; // path to many result to calc average - std::string customdata; // message to custom lib - }; - - ResultProcessor substituteResult; // descriptor how to process the result returned by url with substitutes - std::vector manyResults; // descriptor how to process results returned by url with no substitutes (could be many values for many symbols in the result json in this case) - - uint32_t interval; // poll interval - uint32_t multiplier; // multiplier to convert price value from float to integer -}; - - -struct CCustomProcessor { - CustomJsonParser parser; - CustomClamper clamper; // custom prices clamper - CustomValidator validator; // custom prices validator - CustomConverter converter; // custom prices validator - - int32_t b, e; // price index range begin (inclusive) and end (exclusive) -}; - - -bool PricesFeedParseConfig(const cJSON *json); -bool PricesInitStatuses(); -uint32_t PricesFeedPoll(uint32_t *pricevalues, const uint32_t maxsize, uint32_t *timestamp); -char *PricesFeedSymbolName(char *name, int32_t ind); -int64_t PricesFeedMultiplier(int32_t ind); -int32_t PricesFeedSymbolsCount(); -void PricesFeedSymbolsForMagic(std::string &names, bool compatible); -void PricesAddOldForexConfig(const std::vector &ac_forex); -void PricesAddOldPricesConfig(const std::vector &ac_prices); -void PricesAddOldStocksConfig(const std::vector &ac_stocks); - -void PricesFeedGetCustomProcessors(std::vector &priceProcessors); - - -#endif // #ifndef __PRICES_FEED__ \ No newline at end of file diff --git a/src/cc/priceslibs/Makefile b/src/cc/priceslibs/Makefile deleted file mode 100644 index 872a4a29337..00000000000 --- a/src/cc/priceslibs/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -CXXFLAGS = -fPIC -I../.. -CXXFLAGS_WIN = -I../.. -all: libpricessampleparser.so libnistrandomparser.so -dll: libpricessampleparser.dll libnistrandomparser.dll - -libpricessampleparser.so: PricesResultParserSample.cpp cjsonpointer.cpp cjsonpointer.h ../../cJSON.c ../../cJSON.h priceslibs.h - $(CXX) $(CXXFLAGS) -shared PricesResultParserSample.cpp cjsonpointer.cpp ../../cJSON.c -o $@ -std=c++11 - -libpricessampleparser.dll: PricesResultParserSample.cpp cjsonpointer.cpp cjsonpointer.h ../../cJSON.c ../../cJSON.h priceslibs.h - $(CXX) $(CXXFLAGS_WIN) -shared PricesResultParserSample.cpp cjsonpointer.cpp ../../cJSON.c -o $@ -std=c++11 -static-libgcc -static-libstdc++ - -libnistrandomparser.so: NistRandomParser.cpp cjsonpointer.cpp cjsonpointer.h ../../cJSON.c ../../cJSON.h priceslibs.h - $(CXX) $(CXXFLAGS) -shared NistRandomParser.cpp cjsonpointer.cpp ../../cJSON.c -o $@ -std=c++11 - -libnistrandomparser.dll: NistRandomParser.cpp cjsonpointer.cpp cjsonpointer.h ../../cJSON.c ../../cJSON.h priceslibs.h - $(CXX) $(CXXFLAGS_WIN) -shared NistRandomParser.cpp cjsonpointer.cpp ../../cJSON.c -o $@ -std=c++11 -static-libgcc -static-libstdc++ - -clean: - rm libpricessampleparser.* libnistrandomparser.*. \ No newline at end of file diff --git a/src/cc/priceslibs/NistRandomParser.cpp b/src/cc/priceslibs/NistRandomParser.cpp deleted file mode 100644 index 88cfd055f13..00000000000 --- a/src/cc/priceslibs/NistRandomParser.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/****************************************************************************** -* Copyright © 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ - -// NistRandomParser.cpp lib for prices DTO module that parses NIST service (https://beacon.nist.gov/) random values into 8 x 32-bit numbers - -#include -#include -#include -#include -#include -#include "cJSON.h" -#include "priceslibs.h" -#include "cjsonpointer.h" - -const int32_t EARLY_CHAIN_HEIGHT = 1000; -const uint32_t timestampIndex1 = 1532373960; // 2018-07-23T19:26:00 for pulseIndex=1 chain=1 - -// custom parse nist random value and divide it into prices 32-bit -extern "C" int PF_EXPORT_SYMBOL pricesJsonParser(const char *sjson /*in*/, const char *symbol /*in*/, const char *customdata, uint32_t multiplier /*in*/, uint32_t *value /*out*/) -{ - std::string errorstr; - if (symbol == NULL) { - std::cerr << __func__ << "\t" << "error: null symbol" << std::endl; - return 0; - } - - if (customdata == NULL) { - std::cerr << __func__ << "\t" << "error: null custom data" << std::endl; - return 0; - } - - cJSON *json = cJSON_Parse(sjson); - if (json == NULL) { - std::cerr << __func__ << "\t" << "error: can't parse json" << std::endl; - return 0; - } - - bool r = false; - if (strcmp(symbol, "pulseIndex") == 0) - { - const cJSON *jfound = SimpleJsonPointer(json, customdata, errorstr); - if (jfound && cJSON_IsNumber(jfound)) - { - *value = (uint32_t)(jfound->valuedouble); - r = true; - } - else - std::cerr << __func__ << "\t" << "error: can't found pulseIndex json pointer as number:" << customdata << " :" << errorstr << std::endl; - - } - // check pulseData0...pulseData15 format - else if (strlen(symbol) > 9 && strlen(symbol) <= 11 && strncmp(symbol, "pulseData", 9) == 0 && atoi(&symbol[9]) >= 0 && atoi(&symbol[9]) <= 15) - { - const cJSON *jfound = SimpleJsonPointer(json, customdata, errorstr); - if (jfound && cJSON_IsString(jfound) && strlen(jfound->valuestring) == 512 / 8 * 2) // 512-bit number in hex - { - std::string str256 = std::string(jfound->valuestring); - *value = (uint32_t) std::stoul(str256.substr(atoi(&symbol[9])*8, 8), NULL, 16); // parse 4-byte part - r = true; - } - else - std::cerr << __func__ << "\t" << "error: pulseData value is not a valid 512-bit value as hex string" << std::endl; - } - else - std::cerr << __func__ << "\t" << "error: unsupported symbol, should be 'pulseIndex' or 'pulseData0'..'pulseData15'" << std::endl; - - cJSON_free(json); - return r ? 1 : 0; -} - -// validate nist random value -extern "C" int PF_EXPORT_SYMBOL pricesValidator(int32_t height, uint32_t blocktime, uint32_t timestampBlock, uint32_t timestampPrevBlock, uint32_t prices[], uint32_t prevprices[], int32_t beginpos, int32_t endpos) -{ - static std::map randomCache; - - if (prices == NULL) - { - std::cerr << __func__ << " prices array null" << std::endl; - return -1; - } - if (endpos - beginpos != 1+16) - { - std::cerr << __func__ << " invalid NIST random values count" << std::endl; - return -1; - } - - uint32_t pulseIndex = prices[beginpos]; - uint32_t pulseIndexPrev = prevprices[beginpos]; - - uint32_t parts[16]; - for (int i = 0; i < sizeof(parts)/sizeof(parts[0]); i++) - parts[i] = prices[beginpos + 1 + i]; - - if (randomCache.find(pulseIndex) == randomCache.end()) - { - if (randomCache.size() >= 10) // max num of tracked pulses is 10 - { - // remove lru element: - uint32_t minIndex = (*randomCache.begin()).first; - for (const auto &r : randomCache) - if (r.first < minIndex) - minIndex = r.first; - randomCache.erase(minIndex); - } - - // store the current value - memcpy(randomCache[prices[beginpos]], parts, sizeof(parts)); - } - else - { - // check the current value is not changed - if (memcmp(randomCache[pulseIndex], parts, sizeof(parts)) != 0) - { - std::cerr << __func__ << " invalid NIST random value for pulseIndex=" << pulseIndex << std::endl; - return -1; - } - } - - // validate current value against blocktime: - //uint32_t timestampRandomPrev = timestampIndex1 + (pulseIndexPrev - 1) * 60; - uint32_t timestampRandom = timestampIndex1 + (pulseIndex - 1) * 60; - uint32_t timestampRandomExpected = timestampPrevBlock + blocktime; - - - if (timestampRandom < timestampRandomExpected - 60 && height > EARLY_CHAIN_HEIGHT) - { - std::cerr << __func__ << " invalid NIST random value timestamp is too close for pulseIndex=" << pulseIndex << std::endl; - return -1; - } - - if (timestampRandom > timestampRandomExpected + 60) - { - std::cerr << __func__ << " invalid NIST random value timestamp is too far for pulseIndex=" << pulseIndex << std::endl; - return -1; - } - - return 0; -} - -// empty clamper -extern "C" void PF_EXPORT_SYMBOL pricesClamper(int32_t height, uint32_t prices[], uint32_t prevprices[], int32_t beginpos, int32_t endpos, int64_t tolerance) -{ - return; // no clamping for NIST random data -} - -// trivial converter -extern "C" void PF_EXPORT_SYMBOL pricesConverter(int32_t index, uint32_t storedvalue, int64_t *converted) -{ - if (converted) - *converted = storedvalue; -} \ No newline at end of file diff --git a/src/cc/priceslibs/PricesResultParserSample.cpp b/src/cc/priceslibs/PricesResultParserSample.cpp deleted file mode 100644 index 356ef9c9ac6..00000000000 --- a/src/cc/priceslibs/PricesResultParserSample.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/****************************************************************************** -* Copyright © 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ - -// PricesResultParserSample.cpp sample lib for prices cc that parses the returned json - -#include -#include -#include "cJSON.h" -#include "priceslibs.h" -#include "cjsonpointer.h" - -extern "C" int PF_EXPORT_SYMBOL pricesJsonParser(const char *sjson /*in*/, const char *symbol /*in*/, const char *customdata, uint32_t multiplier /*in*/, uint32_t *value /*out*/) -{ - std::string errorstr; - cJSON *json = cJSON_Parse(sjson); - if (json == NULL) { - std::cerr << __func__ << "\t" << "can't parse json" << std::endl; - return 0; - } - - // ignore symbol and use custom data as json pointer: - const cJSON *jfound = SimpleJsonPointer(json, customdata, errorstr); - bool r = false; - if (jfound == NULL) { - std::cerr << __func__ << "\t" << "can't found json pointer:" << errorstr << std::endl; - } - else if (cJSON_IsNumber(jfound)) { - *value = (uint32_t)(jfound->valuedouble * (double)multiplier); - r = true; - } - else { - std::cerr << __func__ << "\t" << "value is not a number" << std::endl; - } - cJSON_free (json); - return r ? 1 : 0; -} - diff --git a/src/cc/priceslibs/cjsonpointer.c b/src/cc/priceslibs/cjsonpointer.c deleted file mode 100644 index 2738f8e7290..00000000000 --- a/src/cc/priceslibs/cjsonpointer.c +++ /dev/null @@ -1,230 +0,0 @@ -/****************************************************************************** -* Copyright © 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ - -// cjsonpointer.c -// C-language RFC 6901 JSON pointer implementation for cJSON parser - -#include -#include -#include -#include -#include "cjsonpointer.h" - -#define JP_MAXDEPTH 64 -#define TRUE 1 -#define FALSE 0 - - -#define ERR_JSONPOINTER(msg) strncpy(errorstr, msg, CJP_ERRMSGLENGTH-1), errorstr[CJP_ERRMSGLENGTH-1]='\0', (const cJSON*)NULL; - -// unescape json pointer as RFC 6901 requires -static void junescape(char *s) -{ - char *pos; - pos = strstr(s, "~1"); - while (pos) { - *pos = '/'; - strcpy(pos+1, pos + 2); - pos = strstr(pos+2, "~1"); - } - - // must be in second order to prevent accidental escaping effect like "~01" --> "~1": - pos = strstr(s, "~0"); - while (pos) { - *pos = '~'; - strcpy(pos+1, pos + 2); - pos = strstr(pos + 2, "~0"); - } -} - -// check if string is a int number -static int isStringInt(const char *s) -{ - int count = 0; - size_t len = strlen(s); - while (*s && (*s == '+' || *s == '-' || isdigit(*s))) s++,count++; - return (count == len); -} - -// browse json recursively -const cJSON* browseOnLevel(const cJSON *json, char *tokens[], int curtoken, int numtokens, char errorstr[]) -{ - if (cJSON_IsNull(json)) - return ERR_JSONPOINTER("json is null"); - - if (curtoken == numtokens) - return json; - - // char *p = cJSON_Print(json); - // fprintf(stderr, "json=%s\n", (p ? p : "NULL") ); - // if (p) cJSON_free(p); - - if (cJSON_IsArray(json)) - { - if (!isStringInt(tokens[curtoken])) - return ERR_JSONPOINTER("should be numeric array index"); - - int index = atoi(tokens[curtoken++]); - - if (index >= 0 && index < cJSON_GetArraySize(json)) { - const cJSON *item = cJSON_GetArrayItem(json, index); - if (curtoken == numtokens) - return item; - else - return browseOnLevel(item, tokens, curtoken, numtokens, errorstr); - } - else - return ERR_JSONPOINTER("array index out of range"); - } - else if (cJSON_IsObject(json)) // object - { - const cJSON *item = cJSON_GetObjectItem(json, tokens[curtoken]); - if (item) { - curtoken++; - if (curtoken == numtokens) - return item; - else - return browseOnLevel(item, tokens, curtoken, numtokens, errorstr); - } - else - return ERR_JSONPOINTER("json pointer not found (no such item in object)"); - } - else { // property - return ERR_JSONPOINTER("json pointer not found (json branch end reached)"); - } - return ERR_JSONPOINTER("unexpected code reached"); -} - - -// simple json pointer parser as RFC 6901 defines it -// returns json object or property specified by the pointer or NULL -// pointer format examples: -// /object1/object2/property -// /array/index/property (index is zero-based) -// /array/index -// supports escaping of "~" with "~0" and "/" with "~1" -const cJSON *SimpleJsonPointer(const cJSON *json, const char *pointer, char errorstr[]) -{ - char * tokens[JP_MAXDEPTH]; - int numtokens = 0; - - // parse 'path': - const char *b = pointer; - if (*b != '/') - return ERR_JSONPOINTER("json pointer should be prefixed by /"); - b++; - const char *e = b; - while (1) - { - if (!*e || *e == '/') - { - char *token = (char*)malloc(e-b+1); - strncpy(token, b, e - b); - token[e - b] = '\0'; - junescape(token); - tokens[numtokens++] = token; - - if (numtokens == JP_MAXDEPTH) - return ERR_JSONPOINTER("json pointer too deep"); - - if (!*e) - break; - - b = e + 1; - } - e++; - } - - // fprintf(stderr, "tokens:"); - // for (int i = 0; i < numtokens; i++) fprintf(stderr, "%s ", tokens[i]); - // fprintf(stderr, "\n"); - - const cJSON *foundjson = browseOnLevel(json, tokens, 0, numtokens, errorstr); - for (int i = 0; i < numtokens; i++) - free(tokens[i]); - - return foundjson; -} - -/* -// tests for SimpleJsonPointer: -int main() -{ - const char *examples[] = { - "{}", - "{ \"foo\": [\"bar\", \"baz\"], \"xx\": {\"yy\": 111 } }", - "{ \"foo\": [ [\"bar\", \"AAA\", \"BBB\"] ], \"boo\": [\"xx\", {\"yy\": 111 } ] }", - "{[\"ppp\":{}]}", // bad json - "{" - "\"foo\": [\"bar\", \"baz\" ]," - "\"\" : 0," - "\"a/b\" : 1," - "\"c%d\" : 2," - "\"e^f\" : 3," - "\"g|h\" : 4," - "\"i\\\\j\" : 5," - "\"k\\\"l\" : 6," - "\" \" : 7," - "\"m~n\" : 8" - "}" - }; - - typedef struct { - const char *ptr; - int result; - } testcase; - - testcase t0[] = { { "", FALSE },{ "/foo", FALSE }, {NULL, 0} }; - testcase t1[] = { { "", FALSE },{ "/foo", TRUE },{ "/foo/0", TRUE },{ "/foo/0/0", FALSE },{ "/foo/1", TRUE },{ "/foo/2", FALSE },{ "/foo/xx", FALSE },{ "/foo/1/xx", FALSE },{ "/xx/yy", TRUE },{ "/xx/yy/0", FALSE },{ NULL, 0 } }; - testcase t2[] = { { "", FALSE },{ "/foo", TRUE },{ "/foo/0", TRUE },{ "/foo/0/0", TRUE },{ "/foo/1", FALSE },{ "/boo/xx", FALSE },{ "/boo/0", TRUE },{ "/boo/1", TRUE },{ "/boo/1/yy", TRUE },{ "/boo/yy/0", FALSE },{ NULL, 0 } }; - testcase t3[] = { {NULL, FALSE} }; - testcase t4[] = { - { "/", TRUE }, // RFC 6901 requires to return "0" for 'foo' json sample. We do! - { "/a~1b", TRUE }, - { "/c%d", TRUE }, - { "/e^f", TRUE }, - { "/g|h", TRUE }, - { "/i\\j", TRUE }, - { "/k\"l", TRUE }, - { "/ ", TRUE }, - { "/m~n", TRUE }, - { NULL, 0 } - }; - - testcase *cases[] = { t0,t1,t2,t3,t4 }; - - for (int i = 0; i < sizeof(examples) / sizeof(examples[0]); i++) - { - fprintf(stderr, "\nparse object i=%d: ", i); - cJSON *json = cJSON_Parse(examples[i]); - if (!json) { - fprintf(stderr, "json is NULL\n"); - continue; - } - char *p = cJSON_Print(json); - fprintf(stderr, "%s\n", p); - if (p) cJSON_free(p); - - - for (int j = 0; cases[i][j].ptr; j++) { - const cJSON *res = SimpleJsonPointer(json, cases[i][j].ptr); - char *p = (res ? cJSON_Print(res) : NULL); - fprintf(stderr, "for ptr: \"%s\" json: %s, test: %s\n", cases[i][j].ptr, (p ? p : "NULL"), ((!!cases[i][j].result == !!(res != NULL)) ? "ok" : "failed")); - if (p) cJSON_free(p); - } - cJSON_Delete(json); - } -} -*/ \ No newline at end of file diff --git a/src/cc/priceslibs/cjsonpointer.cpp b/src/cc/priceslibs/cjsonpointer.cpp deleted file mode 100644 index e769bcc7b68..00000000000 --- a/src/cc/priceslibs/cjsonpointer.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/****************************************************************************** -* Copyright © 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ - -// cjsonpointer.cpp -// CPP-language RFC 6901 JSON pointer implementation for cJSON parser - -#include -#include -#include -#include -#include -#include -#include -#include -#include "cjsonpointer.h" - -template -static cJSON *reportJsonPointerErr(T errToStream) { - std::ostringstream stream; - errToStream(stream); - std::cerr << stream.str() << std::endl; - return NULL; -} - -// #define ERR_JSONPOINTER(streamexp) reportJsonPointerErr([=](std::ostringstream &stream){ streamexp; }) -#define ERR_JSONPOINTER(msg) errorstr = msg, (const cJSON*)NULL; - - -// unescape json pointer as RFC 6901 requires -static void junescape(std::string &s) -{ - size_t mpos; - mpos = s.find("~1"); - while (mpos != std::string::npos) { - s.replace(mpos, 2, "/"); - mpos = s.find("~1"); - } - - // must be in second order to prevent accidental escaping effect like "~01" --> "~1": - mpos = s.find("~0"); - while (mpos != std::string::npos) { - s.replace(mpos, 2, "~"); - mpos = s.find("~0"); - } -} - -// check if string is a int -static bool is_string_int(const std::string &s) -{ - const char *p = s.c_str(); - int count = 0; - while (*p && (*p =='+' || *p== '-' || isdigit(*p))) p++, count++; - return (count > 0 && count == s.length()); -} - -// simple json pointer parser as RFC 6901 defines it -// returns json object or property specified by the pointer or NULL -// pointer format examples: -// /object1/object2/property -// /array/index/property (index is zero-based) -// /array/index -// supports escaping of "~" with "~0" and "/" with "~1" -const cJSON *SimpleJsonPointer(const cJSON *json, const char *pointer, std::string &errorstr) -{ - std::list tokens; - - // parse 'path': - const char *b = pointer; - if (*b != '/') - return ERR_JSONPOINTER("json pointer should be prefixed by /"); - b++; - const char *e = b; - while (1) { - //const char *e0 = e; - if (!*e || *e == '/') { - // if (b < e) { -- allow empty "" properties - std::string token(b, e); - junescape(token); - tokens.push_back(token); - if (!*e) - break; - //} - b = e + 1; - } - e++; - } - - //std::cerr << "tokens:"; - //for(auto l:tokens) std::cerr << l << " "; - //std::cerr << std::endl; - - // lambda to browse json recursively - std::function browseOnLevel = [&](const cJSON *json)->const cJSON* - { - if (cJSON_IsNull(json)) - return ERR_JSONPOINTER("json pointer: json is null"); - - if (tokens.empty()) - return json; - - //char *p=cJSON_Print(json); - //std::cerr << "json on level:"<< (p?*p:"NULL") << std::endl; - //if (p) cJSON_free(p); - if (cJSON_IsArray(json)) - { - if (!is_string_int(tokens.front())) - return ERR_JSONPOINTER("json pointer: should be numeric array index"); - - int32_t index = atoi( tokens.front().c_str() ); - tokens.pop_front(); - - if (index >= 0 && index < cJSON_GetArraySize(json)) { - const cJSON *item = cJSON_GetArrayItem(json, index); - if (tokens.empty()) - return item; - else - return browseOnLevel(item); - } - else - return ERR_JSONPOINTER("json pointer: array index out of range"); - } - else if (cJSON_IsObject(json)) // object - { - const cJSON *item = cJSON_GetObjectItem(json, tokens.front().c_str()); - if (item) { - tokens.pop_front(); - if (tokens.empty()) - return item; - else - return browseOnLevel(item); - } - else - return ERR_JSONPOINTER("json pointer not found (no such item in object)"); - } - else { // property - return ERR_JSONPOINTER("json pointer not found (json branch end reached)"); - } - return ERR_JSONPOINTER("json pointer: unexpected code reached"); - }; - - return browseOnLevel(json); -} \ No newline at end of file diff --git a/src/cc/priceslibs/cjsonpointer.h b/src/cc/priceslibs/cjsonpointer.h deleted file mode 100644 index 3891b337914..00000000000 --- a/src/cc/priceslibs/cjsonpointer.h +++ /dev/null @@ -1,25 +0,0 @@ -/****************************************************************************** -* Copyright © 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ - -// cjsonpointer.h -// C-language RFC 6901 JSON pointer implementation for cJSON parser -#ifndef __CJSONPOINTER_H__ -#define __CJSONPOINTER_H__ - -#include -#include "cJSON.h" -const cJSON *SimpleJsonPointer(const cJSON *json, const char *pointer, std::string &serror); - -#endif // #ifndef __CJSONPOINTER_H__ \ No newline at end of file diff --git a/src/cc/priceslibs/priceslibs.h b/src/cc/priceslibs/priceslibs.h deleted file mode 100644 index 50a8f736572..00000000000 --- a/src/cc/priceslibs/priceslibs.h +++ /dev/null @@ -1,72 +0,0 @@ -/****************************************************************************** -* Copyright © 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ - -#ifndef __PRICESLIBS__ -#define __PRICESLIBS__ - -#include - -#define PF_CUSTOM_PARSER_FUNCNAME "pricesJsonParser" -#define PF_CUSTOM_VALIDATOR_FUNCNAME "pricesValidator" -#define PF_CUSTOM_CLAMPER_FUNCNAME "pricesClamper" -#define PF_CUSTOM_CONVERTER_FUNCNAME "pricesConverter" - -#ifdef _WIN32 -#define PF_EXPORT_SYMBOL __declspec(dllexport) __stdcall -#else /* !_WIN32 */ -#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) -#define PF_EXPORT_SYMBOL __attribute__((visibility("default"))) -#else -#define PF_EXPORT_SYMBOL -#endif -#endif - - -// custom parsing function -// sjson - json from the polled web resource -// symbol - symbol to parse -// customdata - custom data configured in ac_feed to help with parsing -// multiplier - multiplier configured in ac_feed used to convert the parsed value into integer -// value - pointer to output value -// return 1 if ok and 0 if could not parse -typedef int (*CustomJsonParser)(const char *sjson /*in*/, const char *symbol /*in*/, const char *customdata, uint32_t multiplier /*in*/, uint32_t *value /*out*/); - -// custom validating function -// height - current block height -// blocktime - chain blocktime -// timestampBlock - current block timestamp -// timestampPrevBlock - previous block timestamp -// prices[] current prices values -// prevprices[] previous block prices values -// beginpos beginning position in prices for this ac_feed resource -// endpos ending position in prices for this ac_feed resource (exclusive, like in c++ iterators) -// returns 0 if ok and -1 if prices values are not valid -typedef int (*CustomValidator)(int32_t height, uint32_t blocktime, uint32_t timestampBlock, uint32_t timestampPrev, uint32_t prices[], uint32_t prevprices[], int32_t beginpos, int32_t endpos); - -// custom clamping function -// height - current block height -// prices[] current prices values -// prevprices[] previous block prices values -// beginpos beginning position in prices for this ac_feed resource -// endpos ending position in prices for this ac_feed resource (exclusive, like in c++ iterators) -typedef void (*CustomClamper)(int32_t height, uint32_t prices[], uint32_t prevprices[], int32_t beginpos, int32_t endpos, int64_t tolerance); - -// custom converting function to convert the returned value in satoshi -// index - price value index in prices array (starting with 1) -// storedvalue - value as it is stored in blockchain (usually normalized to integer) -// converted - output value -typedef void (*CustomConverter)(int32_t index, uint32_t storedvalue, int64_t *converted); - -#endif // #ifndef __PRICESLIBS__ diff --git a/src/cc/priceslibs/readme.txt b/src/cc/priceslibs/readme.txt deleted file mode 100644 index ea5fe3a0574..00000000000 --- a/src/cc/priceslibs/readme.txt +++ /dev/null @@ -1,6 +0,0 @@ -This is a directory for the placement of 'Prices' module custom result json parsers. -A custom parser is a shared library that implements a single C function defined in priceslibs.h file. -To use it, a custom parser library should be specified in -ac_feeds command line komodod parameter. -Examples of custom parsers are PricesResultParserSample.cpp and NistRandomParser.cpp sources (there is also a Makefile for building these samples). - -For more information see 'Prices' module documentation on http://developers.komodoplatform.com web site. diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index 63acd583589..df1ab6871b7 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -472,7 +472,7 @@ bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewa char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx; std::vector txids; GetCCaddress(cp,CCaddr,rewardspk); - SetCCtxids(txids,CCaddr,true,cp->evalcode,0,zeroid,'F'); + SetCCtxids(txids,CCaddr,true,cp->evalcode,zeroid,'F'); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { //int height = it->first.blockHeight; @@ -532,7 +532,7 @@ UniValue RewardsList() { UniValue result(UniValue::VARR); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; uint64_t sbits,APR,minseconds,maxseconds,mindeposit; char str[65]; cp = CCinit(&C,EVAL_REWARDS); - SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,0,zeroid,'F'); + SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,zeroid,'F'); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) { txid = *it; diff --git a/src/cc/rogue/main.c b/src/cc/rogue/main.c index f278a77facf..093b73d703d 100644 --- a/src/cc/rogue/main.c +++ b/src/cc/rogue/main.c @@ -277,7 +277,7 @@ void *OS_loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) { fclose(fp); *lenp = 0; - //printf("OS_loadfile null size.(%s)\n",fname); + printf("OS_loadfile null size.(%s)\n",fname); return(0); } if ( filesize > buflen ) diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index e7ba9d3135e..04d0d9e1f7f 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -170,18 +170,18 @@ CScript rogue_highlanderopret(uint8_t funcid,uint256 gametxid,int32_t regslot,CP uint8_t rogue_highlanderopretdecode(uint256 &gametxid, uint256 &tokenid, int32_t ®slot, CPubKey &pk, std::vector &playerdata, std::string &symbol, std::string &pname,CScript scriptPubKey) { std::string name, description; std::vector vorigPubkey; - std::vector oprets, opretsDummy; + std::vector> oprets, opretsDummy; std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; uint8_t e, f,*script; std::vector voutPubkeys; tokenid = zeroid; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( script[1] == 'c' && (f= DecodeTokenCreateOpRetV1(scriptPubKey,origpubkey,name,description, oprets)) == 'c' ) + if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description, oprets)) == 'c' ) { - GetOpReturnCCBlob(oprets, vopretNonfungible); + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); vopret = vopretNonfungible; } - else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRetV1(scriptPubKey, tokenid, voutPubkeys, opretsDummy)) != 0 ) + else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, opretsDummy)) != 0 ) { //fprintf(stderr,"decode opret %c tokenid.%s\n",script[1],tokenid.GetHex().c_str()); GetNonfungibleData(tokenid, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx @@ -209,20 +209,20 @@ uint8_t rogue_keystrokesopretdecode(uint256 &gametxid,uint256 &batontxid,CPubKey uint8_t rogue_registeropretdecode(uint256 &gametxid,uint256 &tokenid,uint256 &playertxid,CScript scriptPubKey) { std::string name, description; std::vector vorigPubkey; - std::vector oprets; + std::vector> oprets; std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; uint8_t e, f,*script; std::vector voutPubkeys; tokenid = zeroid; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( script[1] == 'c' && (f= DecodeTokenCreateOpRetV1(scriptPubKey,origpubkey,name,description,oprets)) == 'c' ) + if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description,oprets)) == 'c' ) { - GetOpReturnCCBlob(oprets, vopretNonfungible); + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); vopret = vopretNonfungible; } - else if ( script[1] != 'R' && (f= DecodeTokenOpRetV1(scriptPubKey, tokenid, voutPubkeys, oprets)) != 0 ) + else if ( script[1] != 'R' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, oprets)) != 0 ) { - GetOpReturnCCBlob(oprets, vopretDummy); // blob from non-creation tx opret + GetOpretBlob(oprets, OPRETID_ROGUEGAMEDATA, vopretDummy); // blob from non-creation tx opret vopret = vopretDummy; } if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> playertxid) != 0 && e == EVAL_ROGUE && f == 'R' ) @@ -898,15 +898,15 @@ UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) CCaddr1of2set(cp,roguepk,roguepk,cp->CCpriv,destaddr); mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode, 1, burnpk)); - uint8_t funcid; uint256 tid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0; + uint8_t e, funcid; uint256 tid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0; CScript opretRegister = rogue_registeropret(gametxid, playertxid); if ( playertxid != zeroid ) { voutPubkeysEmpty.push_back(burnpk); if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 ) { - std::vector oprets; - if ( (funcid= DecodeTokenOpRetV1(playertx.vout.back().scriptPubKey, tid, voutPubkeys, oprets)) != 0) + std::vector> oprets; + if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tid, voutPubkeys, oprets)) != 0) { // if token in the opret didtx = 1; if ( funcid == 'c' ) @@ -914,7 +914,7 @@ UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) vscript_t vopretRegister; GetOpReturnData(opretRegister, vopretRegister); rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, - EncodeTokenOpRetV1(tid, voutPubkeysEmpty /*=never spent*/, { vopretRegister })); + EncodeTokenOpRet(tid, voutPubkeysEmpty /*=never spent*/, std::make_pair(OPRETID_ROGUEGAMEDATA, vopretRegister))); } } } @@ -1458,7 +1458,7 @@ UniValue rogue_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) mypk = pubkey2pk(Mypubkey()); GetCCaddress1of2(cp,coinaddr,roguepk,mypk); //SetCCunspents(unspentOutputs,coinaddr); - SetCCtxids(txids,coinaddr,true,cp->evalcode,0,zeroid,'R'); + SetCCtxids(txids,coinaddr,true,cp->evalcode,zeroid,'R'); rogue_univalue(result,"games",-1,-1); for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) //for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index ed29f8469b0..fcae87cb6c4 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -554,7 +554,7 @@ void komodo_setactivation(int32_t height) void *chainparams_commandline() { - //fprintf(stderr,"chainparams_commandline called\n"); + fprintf(stderr,"chainparams_commandline called\n"); CChainParams::CCheckpointData checkpointData; //fprintf(stderr,">>>>>>>> port.%u\n",ASSETCHAINS_P2PPORT); if ( ASSETCHAINS_SYMBOL[0] != 0 ) diff --git a/src/coins.cpp b/src/coins.cpp index c1f088b86d9..92206b65379 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -702,7 +702,7 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const // use the maximum priority for all (partially or fully) shielded transactions. // (Note that coinbase transactions cannot contain JoinSplits, or Sapling shielded Spends or Outputs.) - if (tx.vjoinsplit.size() > 0 || tx.vShieldedSpend.size() > 0 || tx.vShieldedOutput.size() > 0 || tx.IsCoinImport() || tx.IsPegsImport() || tx.IsPriorityCC()) { + if (tx.vjoinsplit.size() > 0 || tx.vShieldedSpend.size() > 0 || tx.vShieldedOutput.size() > 0 || tx.IsCoinImport() || tx.IsPegsImport()) { return MAX_PRIORITY; } diff --git a/src/crypto/common.h b/src/crypto/common.h index 9d2100af98b..75fee1b7937 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -85,6 +85,27 @@ void static inline WriteBE64(unsigned char* ptr, uint64_t x) memcpy(ptr, (char*)&v, 8); } +/** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */ +uint64_t static inline CountBits(uint64_t x) +{ +#if HAVE_DECL___BUILTIN_CLZL + if (sizeof(unsigned long) >= sizeof(uint64_t)) { + return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; + } +#endif +#if HAVE_DECL___BUILTIN_CLZLL + if (sizeof(unsigned long long) >= sizeof(uint64_t)) { + return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; + } +#endif + int ret = 0; + while (x) { + x >>= 1; + ++ret; + } + return ret; +} + int inline init_and_check_sodium() { if (sodium_init() == -1) { @@ -122,5 +143,4 @@ int inline init_and_check_sodium() return 0; } - #endif // BITCOIN_CRYPTO_COMMON_H diff --git a/src/cryptoconditions/src/anon.c b/src/cryptoconditions/src/anon.c index 4487812f79c..2c3581672cb 100644 --- a/src/cryptoconditions/src/anon.c +++ b/src/cryptoconditions/src/anon.c @@ -53,11 +53,8 @@ static void anonToJSON(const CC *cond, cJSON *params) { } -static unsigned char *anonFingerprint(const CC *cond) { - unsigned char *out = calloc(1, 32); - //fprintf(stderr,"anon fingerprint %p %p\n",out,cond->fingerprint); +static void anonFingerprint(const CC *cond, uint8_t *out) { memcpy(out, cond->fingerprint, 32); - return out; } diff --git a/src/cryptoconditions/src/cryptoconditions.c b/src/cryptoconditions/src/cryptoconditions.c index 90eed1ce0e7..ad31e17d7fd 100644 --- a/src/cryptoconditions/src/cryptoconditions.c +++ b/src/cryptoconditions/src/cryptoconditions.c @@ -62,8 +62,8 @@ void appendUriSubtypes(uint32_t mask, unsigned char *buf) { char *cc_conditionUri(const CC *cond) { - unsigned char *fp = cond->type->fingerprint(cond); - if (!fp) return NULL; + unsigned char *fp = calloc(1, 32); + cond->type->fingerprint(cond, fp); unsigned char *encoded = base64_encode(fp, 32); @@ -116,13 +116,13 @@ uint32_t fromAsnSubtypes(const ConditionTypes_t types) { size_t cc_conditionBinary(const CC *cond, unsigned char *buf) { Condition_t *asn = calloc(1, sizeof(Condition_t)); asnCondition(cond, asn); + size_t out = 0; asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Condition, asn, buf, 1000); - if (rc.encoded == -1) { - fprintf(stderr, "CONDITION NOT ENCODED\n"); - return 0; - } + if (rc.encoded == -1) goto end; + out = rc.encoded; +end: ASN_STRUCT_FREE(asn_DEF_Condition, asn); - return rc.encoded; + return out; } @@ -144,10 +144,12 @@ void asnCondition(const CC *cond, Condition_t *asn) { // This may look a little weird - we dont have a reference here to the correct // union choice for the condition type, so we just assign everything to the threshold // type. This works out nicely since the union choices have the same binary interface. + CompoundSha256Condition_t *choice = &asn->choice.thresholdSha256; choice->cost = cc_getCost(cond); - choice->fingerprint.buf = cond->type->fingerprint(cond); choice->fingerprint.size = 32; + choice->fingerprint.buf = calloc(1, 32); + cond->type->fingerprint(cond, choice->fingerprint.buf); choice->subtypes = asnSubtypes(cond->type->getSubtypes(cond)); } diff --git a/src/cryptoconditions/src/ed25519.c b/src/cryptoconditions/src/ed25519.c index 021b978dd46..db0dde78c65 100644 --- a/src/cryptoconditions/src/ed25519.c +++ b/src/cryptoconditions/src/ed25519.c @@ -25,11 +25,10 @@ struct CCType CC_Ed25519Type; -static unsigned char *ed25519Fingerprint(const CC *cond) { +static void ed25519Fingerprint(const CC *cond, uint8_t *out) { Ed25519FingerprintContents_t *fp = calloc(1, sizeof(Ed25519FingerprintContents_t)); - //fprintf(stderr,"ed25519 fingerprint %p %p\n",fp,cond->publicKey); OCTET_STRING_fromBuf(&fp->publicKey, cond->publicKey, 32); - return hashFingerprintContents(&asn_DEF_Ed25519FingerprintContents, fp); + hashFingerprintContents(&asn_DEF_Ed25519FingerprintContents, fp, out); } diff --git a/src/cryptoconditions/src/eval.c b/src/cryptoconditions/src/eval.c index a5a67c767bc..4693a9e6c69 100644 --- a/src/cryptoconditions/src/eval.c +++ b/src/cryptoconditions/src/eval.c @@ -25,11 +25,8 @@ struct CCType CC_EvalType; -static unsigned char *evalFingerprint(const CC *cond) { - unsigned char *hash = calloc(1, 32); - //fprintf(stderr,"evalfingerprint %p %p\n",hash,cond->code); - sha256(cond->code, cond->codeLength, hash); - return hash; +static void evalFingerprint(const CC *cond, uint8_t *out) { + sha256(cond->code, cond->codeLength, out); } @@ -105,7 +102,7 @@ static uint32_t evalSubtypes(const CC *cond) { */ int jsonVerifyEval(CC *cond, void *context) { if (cond->codeLength == 5 && 0 == memcmp(cond->code, "TEST", 4)) { - return cond->code[5]; + return cond->code[4]; } fprintf(stderr, "Cannot verify eval; user function unknown\n"); return 0; diff --git a/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h b/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h index bdb9ae43acf..5a36ba40ba5 100644 --- a/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h +++ b/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h @@ -36,4 +36,4 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons #endif - +#endif diff --git a/src/cryptoconditions/src/internal.h b/src/cryptoconditions/src/internal.h index 5a2ad36084b..e8008084cd6 100644 --- a/src/cryptoconditions/src/internal.h +++ b/src/cryptoconditions/src/internal.h @@ -39,7 +39,7 @@ typedef struct CCType { char name[100]; Condition_PR asnType; int (*visitChildren)(CC *cond, CCVisitor visitor); - unsigned char *(*fingerprint)(const CC *cond); + void (*fingerprint)(const CC *cond, uint8_t *fp); unsigned long (*getCost)(const CC *cond); uint32_t (*getSubtypes)(const CC *cond); CC *(*fromJSON)(const cJSON *params, char *err); @@ -75,7 +75,7 @@ struct CCType *getTypeByAsnEnum(Condition_PR present); */ unsigned char *base64_encode(const unsigned char *data, size_t input_length); unsigned char *base64_decode(const unsigned char *data_, size_t *output_length); -unsigned char *hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp); +void hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp, uint8_t* out); void dumpStr(unsigned char *str, size_t len); int checkString(const cJSON *value, char *key, char *err); int checkDecodeBase64(const cJSON *value, char *key, char *err, unsigned char **data, size_t *size); diff --git a/src/cryptoconditions/src/prefix.c b/src/cryptoconditions/src/prefix.c index ba500b86719..e4ed1263ae3 100644 --- a/src/cryptoconditions/src/prefix.c +++ b/src/cryptoconditions/src/prefix.c @@ -37,13 +37,12 @@ static int prefixVisitChildren(CC *cond, CCVisitor visitor) { } -static unsigned char *prefixFingerprint(const CC *cond) { +static void prefixFingerprint(const CC *cond, uint8_t *out) { PrefixFingerprintContents_t *fp = calloc(1, sizeof(PrefixFingerprintContents_t)); - //fprintf(stderr,"prefixfinger %p %p\n",fp,cond->prefix); - asnCondition(cond->subcondition, &fp->subcondition); // TODO: check asnCondition for safety + asnCondition(cond->subcondition, &fp->subcondition); fp->maxMessageLength = cond->maxMessageLength; OCTET_STRING_fromBuf(&fp->prefix, cond->prefix, cond->prefixLength); - return hashFingerprintContents(&asn_DEF_PrefixFingerprintContents, fp); + hashFingerprintContents(&asn_DEF_PrefixFingerprintContents, fp, out); } diff --git a/src/cryptoconditions/src/preimage.c b/src/cryptoconditions/src/preimage.c index 73f08c76073..f9d723b581c 100644 --- a/src/cryptoconditions/src/preimage.c +++ b/src/cryptoconditions/src/preimage.c @@ -44,11 +44,8 @@ static unsigned long preimageCost(const CC *cond) { } -static unsigned char *preimageFingerprint(const CC *cond) { - unsigned char *hash = calloc(1, 32); - //fprintf(stderr,"preimage %p %p\n",hash,cond->preimage); - sha256(cond->preimage, cond->preimageLength, hash); - return hash; +static void preimageFingerprint(const CC *cond, uint8_t *out) { + sha256(cond->preimage, cond->preimageLength, out); } diff --git a/src/cryptoconditions/src/secp256k1.c b/src/cryptoconditions/src/secp256k1.c index be101e7ad02..cb72d440632 100644 --- a/src/cryptoconditions/src/secp256k1.c +++ b/src/cryptoconditions/src/secp256k1.c @@ -88,11 +88,10 @@ void initVerify() { } -static unsigned char *secp256k1Fingerprint(const CC *cond) { +static void secp256k1Fingerprint(const CC *cond, uint8_t *out) { Secp256k1FingerprintContents_t *fp = calloc(1, sizeof(Secp256k1FingerprintContents_t)); - //fprintf(stderr,"secpfinger %p %p size %d vs %d\n",fp,cond->publicKey,(int32_t)sizeof(Secp256k1FingerprintContents_t),(int32_t)SECP256K1_PK_SIZE); OCTET_STRING_fromBuf(&fp->publicKey, cond->publicKey, SECP256K1_PK_SIZE); - return hashFingerprintContents(&asn_DEF_Secp256k1FingerprintContents, fp); + hashFingerprintContents(&asn_DEF_Secp256k1FingerprintContents, fp, out); } diff --git a/src/cryptoconditions/src/threshold.c b/src/cryptoconditions/src/threshold.c index 04c8eec70e4..29a4d18ddb1 100644 --- a/src/cryptoconditions/src/threshold.c +++ b/src/cryptoconditions/src/threshold.c @@ -94,17 +94,15 @@ static int cmpConditionBin(const void *a, const void *b) { } -static unsigned char *thresholdFingerprint(const CC *cond) { - /* Create fingerprint */ +static void thresholdFingerprint(const CC *cond, uint8_t *out) { ThresholdFingerprintContents_t *fp = calloc(1, sizeof(ThresholdFingerprintContents_t)); - //fprintf(stderr,"thresholdfinger %p\n",fp); fp->threshold = cond->threshold; for (int i=0; isize; i++) { Condition_t *asnCond = asnConditionNew(cond->subconditions[i]); asn_set_add(&fp->subconditions2, asnCond); } qsort(fp->subconditions2.list.array, cond->size, sizeof(Condition_t*), cmpConditionBin); - return hashFingerprintContents(&asn_DEF_ThresholdFingerprintContents, fp); + hashFingerprintContents(&asn_DEF_ThresholdFingerprintContents, fp, out); } diff --git a/src/cryptoconditions/src/utils.c b/src/cryptoconditions/src/utils.c index ff95f62e041..b4d961d6e2c 100644 --- a/src/cryptoconditions/src/utils.c +++ b/src/cryptoconditions/src/utils.c @@ -210,7 +210,7 @@ void jsonAddBase64(cJSON *params, char *key, unsigned char *bin, size_t size) { } -unsigned char *hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp) { +void hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp, uint8_t *out) { unsigned char buf[BUF_SIZE]; asn_enc_rval_t rc = der_encode_to_buffer(asnType, fp, buf, BUF_SIZE); ASN_STRUCT_FREE(*asnType, fp); @@ -218,9 +218,7 @@ unsigned char *hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp) fprintf(stderr, "Encoding fingerprint failed\n"); return 0; } - unsigned char *hash = calloc(1,32); - sha256(buf, rc.encoded, hash); - return hash; + sha256(buf, rc.encoded, out); } diff --git a/src/cryptoconditions/tests/test_failure_modes.py b/src/cryptoconditions/tests/test_failure_modes.py index 59b0b3f2466..435e20c88bd 100644 --- a/src/cryptoconditions/tests/test_failure_modes.py +++ b/src/cryptoconditions/tests/test_failure_modes.py @@ -82,4 +82,25 @@ def test_malleability_checked(): assert not cc_rfb(b'\xa2\x13\xa0\x0f\xa0\x06\x80\x04abcd\xa0\x05\x80\x03abc\xa1\x00') +def test_large_threshold(): + conds = [{ + 'type': "secp256k1-sha-256", + "publicKey": "02D5D969305535AC29A77079C11D4F0DD40661CF96E04E974A5E8D7E374EE225AA" + }] + + for i in range(250): + conds.append({ + "type": "eval-sha-256", + "code": "VEVTVAE" + }) + + r = jsonRPC("encodeCondition", { + "type": "threshold-sha-256", + "subfulfillments": conds, + "threshold": 251 + }) + assert 'error' not in r, r + + + so.cc_conditionUri.restype = ctypes.c_char_p diff --git a/src/deprecation.h b/src/deprecation.h index dde45e22c35..8d8a464f6a5 100644 --- a/src/deprecation.h +++ b/src/deprecation.h @@ -24,7 +24,7 @@ // * Shut down WEEKS_UNTIL_DEPRECATION weeks' worth of blocks after the estimated release block height. // * A warning is shown during the DEPRECATION_WARN_LIMIT worth of blocks prior to shut down. static const int WEEKS_UNTIL_DEPRECATION = 52; -static const int DEPRECATION_HEIGHT = 2200000; +static const int DEPRECATION_HEIGHT = 2900000; //TODO: use [last_season_array_item - 1] + 650.000 for automagic update static const int APPROX_RELEASE_HEIGHT = DEPRECATION_HEIGHT - (WEEKS_UNTIL_DEPRECATION * 7 * 24 * 60); // Number of blocks before deprecation to warn users diff --git a/src/fiat/etomic b/src/fiat/etomic deleted file mode 100755 index 76eb0191c90..00000000000 --- a/src/fiat/etomic +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=ETOMIC $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/k64 b/src/fiat/k64 deleted file mode 100755 index a3b3bc83571..00000000000 --- a/src/fiat/k64 +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=K64 $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/ksb b/src/fiat/ksb deleted file mode 100755 index 450c3b3f6ec..00000000000 --- a/src/fiat/ksb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=KSB $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/kv b/src/fiat/kv deleted file mode 100755 index 997fccb33bb..00000000000 --- a/src/fiat/kv +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=KV $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/our b/src/fiat/our deleted file mode 100755 index 66c77447641..00000000000 --- a/src/fiat/our +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=OUR $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/sec b/src/fiat/sec deleted file mode 100755 index 185a76cf120..00000000000 --- a/src/fiat/sec +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=SEC $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/wlc b/src/fiat/wlc deleted file mode 100755 index ef7c47b9032..00000000000 --- a/src/fiat/wlc +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=WLC $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/zexo b/src/fiat/zexo deleted file mode 100755 index b6fd508f296..00000000000 --- a/src/fiat/zexo +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=ZEXO $1 $2 $3 $4 $5 $6 diff --git a/src/fs.cpp b/src/fs.cpp new file mode 100644 index 00000000000..a5e12f1cfcb --- /dev/null +++ b/src/fs.cpp @@ -0,0 +1,15 @@ +#include "fs.h" + +namespace fsbridge { + +FILE *fopen(const fs::path& p, const char *mode) +{ + return ::fopen(p.string().c_str(), mode); +} + +FILE *freopen(const fs::path& p, const char *mode, FILE *stream) +{ + return ::freopen(p.string().c_str(), mode, stream); +} + +} // fsbridge diff --git a/src/fs.h b/src/fs.h new file mode 100644 index 00000000000..db7d26a6577 --- /dev/null +++ b/src/fs.h @@ -0,0 +1,24 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef KOMODO_FS_H +#define KOMODO_FS_H + +#include +#include + +#include +#include +#include + +/** Filesystem operations and types */ +namespace fs = boost::filesystem; + +/** Bridge operations to C stdio */ +namespace fsbridge { + FILE *fopen(const fs::path& p, const char *mode); + FILE *freopen(const fs::path& p, const char *mode, FILE *stream); +}; + +#endif // KOMODO_FS_H diff --git a/src/gmp_i64.c b/src/gmp_i64.c deleted file mode 100644 index cc64ce8248d..00000000000 --- a/src/gmp_i64.c +++ /dev/null @@ -1,50 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include "gmp_i64.h" - -void mpz_set_si64( mpz_t rop, int64_t op ) -{ - int isneg = op < 0 ? 1 : 0; - int64_t op_abs = op < 0 ? -op : op; - mpz_import(rop, 1, 1, sizeof(op_abs), 0, 0, &op_abs); - if (isneg) - mpz_neg(rop, rop); -} - -int64_t mpz_get_si64( mpz_t op ) -{ - uint64_t u = 0LL; /* if op is zero nothing will be written into u */ - uint64_t u_abs; - - mpz_export(&u, NULL, 1, sizeof(u), 0, 0, op); - u_abs = u < 0 ? -u : u; - if (mpz_sgn(op) < 0) - return -(int64_t)u_abs; - else - return (int64_t)u_abs; -} - -void mpz_set_ui64( mpz_t rop, uint64_t op ) -{ - mpz_import(rop, 1, 1, sizeof(op), 0, 0, &op); -} - -uint64_t mpz_get_ui64( mpz_t op ) -{ - uint64_t u = 0LL; /* if op is zero nothing will be written into u */ - mpz_export(&u, NULL, 1, sizeof(u), 0, 0, op); - return u; -} \ No newline at end of file diff --git a/src/gmp_i64.h b/src/gmp_i64.h deleted file mode 100644 index 99ff7da6c06..00000000000 --- a/src/gmp_i64.h +++ /dev/null @@ -1,31 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include -#include - -#if defined (__cplusplus) -extern "C" { -#endif - - // a small extension to gmp lib for setting/getting int64_t/uint64_t - void mpz_set_si64(mpz_t rop, int64_t op); - int64_t mpz_get_si64(mpz_t op); - void mpz_set_ui64(mpz_t rop, uint64_t op); - uint64_t mpz_get_ui64(mpz_t op); - -#if defined (__cplusplus) -} -#endif diff --git a/src/gtest/test_timedata.cpp b/src/gtest/test_timedata.cpp new file mode 100644 index 00000000000..20e97ddebab --- /dev/null +++ b/src/gtest/test_timedata.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2020 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include +#include + +#include "timedata.h" +#include "random.h" +#include "netbase.h" + +using ::testing::StrictMock; + +class MockCTimeWarning : public CTimeWarning +{ +public: + MOCK_METHOD2(Warn, void(size_t, size_t)); +}; + +CNetAddr GetUniqueAddr() { + uint8_t buf[16] = { 0xFD }; // RFC 4193 address in FC00::/7 with L = 1 (locally assigned) + GetRandBytes(&buf[1], 15); + CNetAddr ip; + ip.SetRaw(NET_IPV6, buf); + assert(ip.IsRFC4193()); + return ip; +} + +TEST(TimeWarning, Assertions) +{ + StrictMock tw; + EXPECT_DEATH(tw.AddTimeData(GetUniqueAddr(), 0, INT64_MIN), ""); + EXPECT_DEATH(tw.AddTimeData(GetUniqueAddr(), 0, -1), ""); + EXPECT_DEATH(tw.AddTimeData(GetUniqueAddr(), 0, INT64_MAX - CTimeWarning::TIMEDATA_IGNORE_THRESHOLD + 1), ""); + EXPECT_DEATH(tw.AddTimeData(GetUniqueAddr(), 0, INT64_MAX), ""); +} + +TEST(TimeWarning, NoWarning) +{ + StrictMock tw; + int64_t now = GetTime(); + + EXPECT_CALL(tw, Warn(CTimeWarning::TIMEDATA_WARNING_SAMPLES, 0)).Times(0); + + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_IGNORE_THRESHOLD, now); + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_IGNORE_THRESHOLD, now); + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_WARNING_THRESHOLD, now); + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_WARNING_THRESHOLD, now); + + for (size_t i = 0; i < CTimeWarning::TIMEDATA_WARNING_SAMPLES - 2; i++) { + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_WARNING_THRESHOLD + 1, now); + } + CNetAddr duplicateIp = GetUniqueAddr(); + for (size_t i = 0; i < 2; i++) { + tw.AddTimeData(duplicateIp, now + CTimeWarning::TIMEDATA_WARNING_THRESHOLD + 1, now); + } +} + +TEST(TimeWarning, PeersAhead) +{ + StrictMock tw; + int64_t now = GetTime(); + + EXPECT_CALL(tw, Warn(CTimeWarning::TIMEDATA_WARNING_SAMPLES, 0)); + + for (size_t i = 0; i < CTimeWarning::TIMEDATA_WARNING_SAMPLES - 1; i++) { + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_WARNING_THRESHOLD + 1, now); + } + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_IGNORE_THRESHOLD - 1, now); +} + +TEST(TimeWarning, PeersBehind) +{ + StrictMock tw; + int64_t now = GetTime(); + + EXPECT_CALL(tw, Warn(0, CTimeWarning::TIMEDATA_WARNING_SAMPLES)); + + for (size_t i = 0; i < CTimeWarning::TIMEDATA_WARNING_SAMPLES - 1; i++) { + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_WARNING_THRESHOLD - 1, now); + } + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_IGNORE_THRESHOLD + 1, now); +} + +TEST(TimeWarning, PeersMixed) +{ + StrictMock tw; + int64_t now = GetTime(); + + EXPECT_CALL(tw, Warn(CTimeWarning::TIMEDATA_WARNING_SAMPLES/2, (CTimeWarning::TIMEDATA_WARNING_SAMPLES+1)/2)); + + for (size_t i = 0; i < CTimeWarning::TIMEDATA_WARNING_SAMPLES/2; i++) { + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_WARNING_THRESHOLD + 1, now); + } + for (size_t i = 0; i < (CTimeWarning::TIMEDATA_WARNING_SAMPLES-1)/2; i++) { + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_WARNING_THRESHOLD - 1, now); + } + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_IGNORE_THRESHOLD + 1, now); +} \ No newline at end of file diff --git a/src/importcoin.cpp b/src/importcoin.cpp index d935f383442..c3da613c8a7 100644 --- a/src/importcoin.cpp +++ b/src/importcoin.cpp @@ -16,7 +16,6 @@ #include "crosschain.h" #include "importcoin.h" #include "cc/utils.h" -#include "cc/CCtokens.h" #include "coins.h" #include "hash.h" #include "script/cc.h" @@ -57,13 +56,13 @@ CTransaction MakeImportCoinTransaction(const ImportProof proof, const CTransacti if (!vopret.empty()) { std::vector vorigpubkey; uint8_t funcId; - std::vector oprets; + std::vector > oprets; std::string name, desc; - if (DecodeTokenCreateOpRetV1(mtx.vout.back().scriptPubKey, vorigpubkey, name, desc, oprets) == 'c') { // parse token 'c' opret + if (DecodeTokenCreateOpRet(mtx.vout.back().scriptPubKey, vorigpubkey, name, desc, oprets) == 'c') { // parse token 'c' opret mtx.vout.pop_back(); //remove old token opret - oprets.push_back(importData); - mtx.vout.push_back(CTxOut(0, EncodeTokenCreateOpRetV1(vorigpubkey, name, desc, oprets))); // make new token 'c' opret with importData + oprets.push_back(std::make_pair(OPRETID_IMPORTDATA, importData)); + mtx.vout.push_back(CTxOut(0, EncodeTokenCreateOpRet('c', vorigpubkey, name, desc, oprets))); // make new token 'c' opret with importData } else { LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeImportCoinTransaction() incorrect token import opret" << std::endl); @@ -137,7 +136,7 @@ CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymb } CTxOut MakeBurnOutput(CAmount value,uint32_t targetCCid,std::string targetSymbol,const std::vector payouts,std::vector rawproof,uint256 pegstxid, - uint256 tokenid,CPubKey srcpub,int64_t amount,std::pair account, CPubKey accountpk) + uint256 tokenid,CPubKey srcpub,int64_t amount,std::pair account) { std::vector opret; opret = E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN; @@ -149,8 +148,7 @@ CTxOut MakeBurnOutput(CAmount value,uint32_t targetCCid,std::string targetSymbol ss << tokenid; ss << srcpub; ss << amount; - ss << account; - ss << accountpk); + ss << account); return CTxOut(value, CScript() << OP_RETURN << opret); } @@ -174,24 +172,24 @@ bool UnmarshalImportTx(const CTransaction importTx, ImportProof &proof, CTransac if (vImportData.begin()[0] == EVAL_TOKENS) { // if it is tokens // get import data after token opret: - std::vector oprets; + std::vector> oprets; std::vector vorigpubkey; std::string name, desc; - if (DecodeTokenCreateOpRetV1(importTx.vout.back().scriptPubKey, vorigpubkey, name, desc, oprets) == 0) { + if (DecodeTokenCreateOpRet(importTx.vout.back().scriptPubKey, vorigpubkey, name, desc, oprets) == 0) { LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() could not decode token opret" << std::endl); return false; } - GetOpReturnImportBlob(oprets, vImportData); // fetch import data after token opret - for (std::vector::const_iterator i = oprets.begin(); i != oprets.end(); i++) - if ((*i).size() > 0 && (*i)[0] == EVAL_IMPORTCOIN) { + GetOpretBlob(oprets, OPRETID_IMPORTDATA, vImportData); // fetch import data after token opret + for (std::vector>::const_iterator i = oprets.begin(); i != oprets.end(); i++) + if ((*i).first == OPRETID_IMPORTDATA) { oprets.erase(i); // remove import data from token opret to restore original payouts: break; } payouts = std::vector(importTx.vout.begin(), importTx.vout.end()-1); //exclude opret with import data - payouts.push_back(CTxOut(0, EncodeTokenCreateOpRetV1(vorigpubkey, name, desc, oprets))); // make original payouts token opret (without import data) + payouts.push_back(CTxOut(0, EncodeTokenCreateOpRet('c', vorigpubkey, name, desc, oprets))); // make original payouts token opret (without import data) } else { //payouts = std::vector(importTx.vout.begin()+1, importTx.vout.end()); // see next @@ -221,15 +219,16 @@ bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint3 } if (vburnOpret.begin()[0] == EVAL_TOKENS) { //if it is tokens - std::vector oprets; + std::vector> oprets; uint256 tokenid; + uint8_t evalCodeInOpret; std::vector voutTokenPubkeys; - if (DecodeTokenOpRetV1(burnTx.vout.back().scriptPubKey, tokenid, voutTokenPubkeys, oprets) != 't') + if (DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) != 't') return false; //skip token opret: - GetOpReturnImportBlob(oprets, vburnOpret); // fetch burnOpret after token opret + GetOpretBlob(oprets, OPRETID_BURNDATA, vburnOpret); // fetch burnOpret after token opret if (vburnOpret.empty()) { LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalBurnTx() cannot unmarshal token burn tx: empty burn opret for tokenid=" << tokenid.GetHex() << std::endl); return false; @@ -293,7 +292,7 @@ bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector> amount)); } -bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &pegstxid,uint256 &tokenid,CPubKey &srcpub, int64_t &amount,std::pair &account, CPubKey &accountpk) +bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &pegstxid,uint256 &tokenid,CPubKey &srcpub, int64_t &amount,std::pair &account) { std::vector burnOpret,rawproof; bool isEof=true; uint32_t targetCCid; uint256 payoutsHash; std::string targetSymbol; @@ -311,8 +310,7 @@ bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &pegstxid,uint256 &tokeni ss >> tokenid; ss >> srcpub; ss >> amount; - ss >> account; - ss >> accountpk)); + ss >> account)); } /* @@ -335,16 +333,17 @@ CAmount GetCoinImportValue(const CTransaction &tx) if (isNewImportTx && vburnOpret.begin()[0] == EVAL_TOKENS) { //if it is tokens + uint8_t evalCodeInOpret; uint256 tokenid; std::vector voutTokenPubkeys; - std::vector oprets; + std::vector> oprets; - if (DecodeTokenOpRetV1(tx.vout.back().scriptPubKey, tokenid, voutTokenPubkeys, oprets) == 0) + if (DecodeTokenOpRet(tx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) == 0) return 0; uint8_t nonfungibleEvalCode = EVAL_TOKENS; // init as if no non-fungibles vscript_t vnonfungibleOpret; - GetOpReturnCCBlob(oprets, vnonfungibleOpret); + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vnonfungibleOpret); if (!vnonfungibleOpret.empty()) nonfungibleEvalCode = vnonfungibleOpret.begin()[0]; diff --git a/src/importcoin.h b/src/importcoin.h index 593e8efbfcb..955ead8257a 100644 --- a/src/importcoin.h +++ b/src/importcoin.h @@ -103,12 +103,12 @@ CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymb CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof,std::string srcaddr, std::string receipt); CTxOut MakeBurnOutput(CAmount value,uint32_t targetCCid,std::string targetSymbol,const std::vector payouts,std::vector rawproof,uint256 pegstxid, - uint256 tokenid,CPubKey srcpub,int64_t amount,std::pair account, CPubKey accountpk); + uint256 tokenid,CPubKey srcpub,int64_t amount,std::pair account); bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector &rawproof); bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt); bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector &publishers,std::vector &txids,uint256& burntxid,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub, int64_t &amount); -bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &pegstxid,uint256 &tokenid,CPubKey &srcpub,int64_t &amount,std::pair &account,CPubKey& accountpk); +bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &pegstxid,uint256 &tokenid,CPubKey &srcpub,int64_t &amount,std::pair &account); bool UnmarshalImportTx(const CTransaction importTx, ImportProof &proof, CTransaction &burnTx,std::vector &payouts); bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state); diff --git a/src/init.cpp b/src/init.cpp index 978f6f8b53c..e53fecf891a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -98,7 +98,6 @@ extern bool komodo_dailysnapshot(int32_t height); extern int32_t KOMODO_LOADINGBLOCKS; extern bool VERUS_MINTBLOCKS; extern char ASSETCHAINS_SYMBOL[]; -extern uint8_t ASSETCHAINS_PUBLIC; extern int32_t KOMODO_SNAPSHOT_INTERVAL; extern void komodo_init(int32_t height); @@ -136,6 +135,9 @@ enum BindFlags { }; static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; + +static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map"; + CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h ////////////////////////////////////////////////////////////////////////////// @@ -409,6 +411,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-spentindex", strprintf(_("Maintain a full spent index, used to query the spending txid and input index for an outpoint (default: %u)"), DEFAULT_SPENTINDEX)); strUsage += HelpMessageGroup(_("Connection options:")); strUsage += HelpMessageOpt("-addnode=", _("Add a node to connect to and attempt to keep the connection open")); + strUsage += HelpMessageOpt("-asmap=", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME)); strUsage += HelpMessageOpt("-banscore=", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), 100)); strUsage += HelpMessageOpt("-bantime=", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), 86400)); strUsage += HelpMessageOpt("-bind=", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6")); @@ -1049,6 +1052,31 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__); } + // Read asmap file if configured + if (mapArgs.count("-asmap")) { + fs::path asmap_path = fs::path(GetArg("-asmap", "")); + if (asmap_path.empty()) { + asmap_path = DEFAULT_ASMAP_FILENAME; + } + if (!asmap_path.is_absolute()) { + asmap_path = GetDataDir() / asmap_path; + } + if (!fs::exists(asmap_path)) { + InitError(strprintf(_("Could not find asmap file %s"), asmap_path)); + return false; + } + std::vector asmap = CAddrMan::DecodeAsmap(asmap_path); + if (asmap.size() == 0) { + InitError(strprintf(_("Could not parse asmap file %s"), asmap_path)); + return false; + } + const uint256 asmap_version = SerializeHash(asmap); + addrman.m_asmap = std::move(asmap); // //node.connman->SetAsmap(std::move(asmap)); + LogPrintf("Using asmap version %s for IP bucketing\n", asmap_version.ToString()); + } else { + LogPrintf("Using /16 prefix for IP bucketing\n"); + } + if (GetBoolArg("-salvagewallet", false)) { // Rewrite just private keys: rescan to find transactions if (SoftSetBoolArg("-rescan", true)) @@ -1398,12 +1426,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if ( KOMODO_NSPV_FULLNODE ) { - if ( ASSETCHAINS_PUBLIC ) { - LogPrintf("Skipping zksnark circuit param loading on ac_public chain\n"); - } else { - // Initialize Zcash circuit parameters - ZC_LoadParams(chainparams); - } + // Initialize Zcash circuit parameters + ZC_LoadParams(chainparams); } /* Start the RPC server already. It will be started in "warmup" mode * and not really process calls already (but it will signify connections @@ -1484,7 +1508,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // -proxy sets a proxy for all outgoing network traffic // -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default std::string proxyArg = GetArg("-proxy", ""); - SetLimited(NET_TOR); + SetLimited(NET_ONION); if (proxyArg != "" && proxyArg != "0") { proxyType addrProxy = proxyType(CService(proxyArg, 9050), proxyRandomize); if (!addrProxy.IsValid()) @@ -1492,9 +1516,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) SetProxy(NET_IPV4, addrProxy); SetProxy(NET_IPV6, addrProxy); - SetProxy(NET_TOR, addrProxy); + SetProxy(NET_ONION, addrProxy); SetNameProxy(addrProxy); - SetLimited(NET_TOR, false); // by default, -proxy sets onion as reachable, unless -noonion later + SetLimited(NET_ONION, false); // by default, -proxy sets onion as reachable, unless -noonion later } // -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses @@ -1503,13 +1527,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) std::string onionArg = GetArg("-onion", ""); if (onionArg != "") { if (onionArg == "0") { // Handle -noonion/-onion=0 - SetLimited(NET_TOR); // set onions as unreachable + SetLimited(NET_ONION); // set onions as unreachable } else { proxyType addrOnion = proxyType(CService(onionArg, 9050), proxyRandomize); if (!addrOnion.IsValid()) return InitError(strprintf(_("Invalid -onion address: '%s'"), onionArg)); - SetProxy(NET_TOR, addrOnion); - SetLimited(NET_TOR, false); + SetProxy(NET_ONION, addrOnion); + SetLimited(NET_ONION, false); } } @@ -1590,20 +1614,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) InitBlockIndex(); SetRPCWarmupFinished(); uiInterface.InitMessage(_("Done loading")); - if ( KOMODO_DEX_P2P != 0 ) - { - void komodo_DEX_init(); - void komodo_DEX_pubkeyupdate(); - komodo_DEX_init(); - nLocalServices |= NODE_DEXP2P; - bool fFirstRun = true; - pwalletMain = new CWallet(GetArg("-wallet", "wallet.dat")); - DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun); - fprintf(stderr,"pwalletMain.%p errors %d DB_LOAD_OK.%d\n",pwalletMain,(int32_t)nLoadWalletRet,(int32_t)DB_LOAD_OK); - komodo_DEX_pubkeyupdate(); - } - if ( pwalletMain == 0 ) - pwalletMain = new CWallet("tmptmp.wallet"); + pwalletMain = new CWallet("tmptmp.wallet"); return !fRequestShutdown; } // ********************************************************* Step 7: load block chain @@ -1993,15 +2004,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) PruneAndFlush(); } } - if ( KOMODO_DEX_P2P != 0 ) - { - void komodo_DEX_init(); - komodo_DEX_init(); - nLocalServices |= NODE_DEXP2P; - if ( KOMODO_DEX_P2P > 1 ) - nLocalServices |= NODE_DEXP2P_INDEXED; - fprintf(stderr,"nLocalServices %llx %d\n",(long long)nLocalServices,KOMODO_DEX_P2P); - } if ( KOMODO_NSPV == 0 ) { if ( GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX) != 0 ) diff --git a/src/komodo-tx.cpp b/src/komodo-tx.cpp index 21752778454..084d99c8049 100644 --- a/src/komodo-tx.cpp +++ b/src/komodo-tx.cpp @@ -200,17 +200,6 @@ int32_t komodo_nextheight() return(100000000); } -// stub to allow to link -int32_t komodo_currentheight() -{ - return(100000000); -} - -// stub to allow to link -uint32_t GetLatestTimestamp(int32_t) -{ - return 0L; -} // Set default values of new CMutableTransaction based on consensus rules at given height. CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight) diff --git a/src/komodo.h b/src/komodo.h index 0f22f2c0981..3176f65929f 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -383,7 +383,7 @@ void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotar fseek(fp,0,SEEK_END); else { - //fprintf(stderr,"komodo_faststateinit retval.%d\n",retval); + fprintf(stderr,"komodo_faststateinit retval.%d\n",retval); while ( komodo_parsestatefile(sp,fp,symbol,dest) >= 0 ) ; } diff --git a/src/komodo_DEX.h b/src/komodo_DEX.h deleted file mode 100644 index 3b5f33e9352..00000000000 --- a/src/komodo_DEX.h +++ /dev/null @@ -1,3173 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2020 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -// included from komodo_nSPV_superlite.h - -/* - MAKE SURE YOU NTP sync your node, precise timestamps are assumed - - _functions() assume DEX_globalmutex is locked when it is called - functions() assume that DEX_globalmutes is not locked when it is called and must lock/unlock to call _functions() - - message format: - - is the datablob for a 'Q' quote or + n * for a 'P' ping of recent shorthashes - - To achieve very fast performance a hybrid push/poll/pull gossip protocol is used. All new quotes are broadcast KOMODO_DEX_RELAYDEPTH levels deep, this gets the quote to large percentage of nodes assuming ^ KOMODO_DEX_RELAYDEPTH power is approx totalnodes/2. Nodes in the broacast "cone" will receive a new quote in less than a second in most cases. - - For efficiency it is better to aim for one third to half of the nodes during the push phase. The higher the KOMODO_DEX_RELAYDEPTH the faster a quote will be broadcast, but the more redundant packets will be sent. If each node would be able to have a full network connectivity map, each node could locally simulate packet propagation and select a subset of peers with the maximum propagation and minimum overlap. However, such an optimization requires up to date and complete network topography and the current method has the advantage of being much simpler and reasonably efficient. - - Additionally, all nodes will be broadcasting to their immediate peers, the most recent quotes not known to be known by the destination, which will allow the receiving peer to find any quotes they are missing and request it directly. At the cost of the local broadcasting, all the nodes will be able to directly request any quote they didnt get during the push phase. - - For sparsely connected nodes, as the pull process propagates a new quote, they will eventually also see the new quote. Worst case would be the last node in a singly connected chain of peers. Assuming most all nodes will have 3 or more peers, then most all nodes will get a quote broadcast in a few multiples of KOMODO_DEX_LOCALHEARTBEAT - - -todo: - permissioned list of pubkeys - ignore lagging REQUEST commands (maybe others too) - updatentz argv[1] -> system(getblockhash) -> extract last N heights, compare to DEX_list, post changed, cancel if reorged. - notarizer post list of active coins, register 01pubkey to 03pubkey/handle/address, scan from last notarization, sortition select, identify forks - - - - the payload is rejected, so it is in the orderbook falsely. i guess i need to check for such wrong senders and not put it in the orderbook, or just reject it completely [wrong sender broadcast] - -payments: - paying a specific "fee" could allow bypassing the txpow requirement (as you proposed with the paywall) - One useful feature might be auction type of selling were highest bidder win if there are no new bids given like an hour or so? DEX_auction - yes, video subscriptions wouldnt be very hard at all - there is no place to upload as the lifetime is one hour, however one thing i wrote above is you can submit (encrypted) data to a service provider who will store it for you for a specified amount of time, with progress payments along the way - - -later: - detect peer restarted and peerclear - defend against memory overflow - defend against pingpong attack with pongbits - shamirs sharding of data - parameterize network #defines heartbeat, maxhops, maxlag, relaydepth, peermasksize, hashlog2, purgetime - detect evil peer: 'Q' is directly protected by txpow, G is a fixed size, so it cant be very big and invalid request can be detected. 'P' message will lead to 'G' queries that cannot be answered, 'R' needs to have high priority - */ - -uint8_t *komodo_DEX_encrypt(uint8_t **allocatedp,uint8_t *data,int32_t *datalenp,bits256 pubkey,bits256 privkey); -uint8_t *komodo_DEX_decrypt(uint8_t *senderpub,uint8_t **allocatedp,uint8_t *data,int32_t *datalenp,bits256 privkey); -void komodo_DEX_pubkey(bits256 &pub0); -void komodo_DEX_privkey(bits256 &priv0); -int32_t komodo_DEX_request(int32_t priority,uint32_t shorthash,uint32_t timestamp,char *tagA,char *tagB); - -#define KOMODO_DEX_PURGELIST 0 - -#define KOMODO_DEX_BLAST (iter/3) // define as iter to make it have 10 different priorities, as 0 to blast diff 0 -#define KOMODO_DEX_ROUTESIZE 6 // (relaydepth + funcid + timestamp) - -#define KOMODO_DEX_LOCALHEARTBEAT 1 -#define KOMODO_DEX_MAXHOPS 10 // most distant node pair after push phase -#define KOMODO_DEX_MAXLAG 60 -#define KOMODO_DEX_RELAYDEPTH ((uint8_t)KOMODO_DEX_MAXHOPS) // increase as root of network size increases -#define KOMODO_DEX_MAXFANOUT ((uint8_t)6) - -#define KOMODO_DEX_HASHLOG2 14 -#define KOMODO_DEX_MAXPERSEC (1 << KOMODO_DEX_HASHLOG2) // effective limit of sustained datablobs/sec -//#define KOMODO_DEX_HASHMASK (KOMODO_DEX_MAXPERSEC - 1) -#define KOMODO_DEX_PURGETIME (3600) -#define KOMODO_DEX_MAXPING (KOMODO_DEX_MAXPERSEC / 17) - -#define KOMOD_DEX_PEERMASKSIZE 128 -#define KOMODO_DEX_MAXPEERID (KOMOD_DEX_PEERMASKSIZE * 8) -#define SECONDS_IN_DAY (24*3600) -#define KOMODO_DEX_PEERPERIOD KOMODO_DEX_PURGETIME // must be evenly divisible into SECONDS_IN_DAY -#define KOMODO_DEX_PEEREPOCHS (SECONDS_IN_DAY / KOMODO_DEX_PEERPERIOD) - -#define KOMODO_DEX_TAGSIZE 16 // (33 / 2) rounded down -#define KOMODO_DEX_MAXKEYSIZE 34 // destpub 1+33, or tagAB 1+16 + 1 + 16 -> both are 34 -#define KOMODO_DEX_MAXINDEX 64 -#define KOMODO_DEX_MAXINDICES 4 // [0] destpub, [1] tagA, [2] tagB, [3] two tags order dependent - -#define KOMODO_DEX_MAXPACKETSIZE (1 << 20) -#define KOMODO_DEX_MAXPRIORITY 32 // a millionX should be enough, but can be as high as 64 - KOMODO_DEX_TXPOWBITS -#define KOMODO_DEX_TXPOWBITS 4 // should be 11 for approx 1 sec per tx -#define KOMODO_DEX_VIPLEVEL 5 // if all are VIP it will try to 100% sync all nodes -#define KOMODO_DEX_CMDPRIORITY (KOMODO_DEX_VIPLEVEL + 2) // minimum extra priority for commands -#define KOMODO_DEX_POLLVIP 30 - -#define KOMODO_DEX_TXPOWDIVBITS 12 // each doubling of size, increases minpriority -#define KOMODO_DEX_TXPOWMASK ((1LL << KOMODO_DEX_TXPOWBITS)-1) -//#define KOMODO_DEX_CREATEINDEX_MINPRIORITY 6 // 64x baseline diff -> approx 1 minute if baseline is 1 second diff - -#define KOMODO_DEX_FILEBUFSIZE 10000 -#define KOMODO_DEX_STREAMSIZE 100 -#define KOMODO_DEX_ANONSIZE 1024 - -#define _komodo_DEXquotehash(hash,len) (uint32_t)(((hash).ulongs[0] >> (KOMODO_DEX_TXPOWBITS + komodo_DEX_sizepriority(len)))) -#define komodo_DEX_id(ptr) _komodo_DEXquotehash(ptr->hash,ptr->datalen) - -#define GENESIS_PUBKEYSTR ((char *)"1259ec21d31a30898d7cd1609f80d9668b4778e3d97e941044b39f0c44d2e51b") -#define GENESIS_PRIVKEYSTR ((char *)"88a71671a6edd987ad9e9097428fc3f169decba3ac8f10da7b24e0ca16803b70") - -struct DEX_datablob -{ - UT_hash_handle hh; - struct DEX_datablob *nexts[KOMODO_DEX_MAXINDICES],*prevs[KOMODO_DEX_MAXINDICES]; - bits256 hash; - uint8_t peermask[KOMOD_DEX_PEERMASKSIZE]; - uint32_t recvtime,cancelled,lastlist,shorthash; - int32_t datalen; - int8_t priority,sizepriority; - uint8_t numsent,offset,linkmask,requested; - uint8_t data[]; -}; - -struct DEX_index_list { struct DEX_datablob *nexts[KOMODO_DEX_MAXINDICES],*prevs[KOMODO_DEX_MAXINDICES]; }; - -struct DEX_index -{ - UT_hash_handle hh; - struct DEX_datablob *head,*tail; - uint8_t keylen; - uint8_t key[KOMODO_DEX_MAXKEYSIZE]; -} *DEX_destpubs,*DEX_tagAs,*DEX_tagBs,*DEX_tagABs; - -struct DEX_orderbookentry -{ - bits256 hash; - double price; - int64_t amountA,amountB; - uint32_t timestamp,shorthash; - uint8_t pubkey33[33],priority; -}; - -// start perf metrics -static double DEX_lag,DEX_lag2,DEX_lag3; -static int64_t DEX_totalsent,DEX_totalrecv,DEX_totaladd,DEX_duplicate,DEX_progress; -static int64_t DEX_lookup32,DEX_collision32,DEX_add32,DEX_maxlag; -static int64_t DEX_Numpending,DEX_freed,DEX_truncated; -// end perf metrics - -static uint32_t Got_Recent_Quote; -bits256 DEX_pubkey,GENESIS_PUBKEY,GENESIS_PRIVKEY; -pthread_mutex_t DEX_globalmutex; - -static struct DEX_globals -{ - int32_t DEX_peermaps[KOMODO_DEX_PEEREPOCHS][KOMODO_DEX_MAXPEERID]; - uint32_t Pendings[KOMODO_DEX_MAXLAG * KOMODO_DEX_MAXPERSEC - 1]; - - struct DEX_datablob *Hashtables[KOMODO_DEX_PURGETIME]; -#if KOMODO_DEX_PURGELIST - struct DEX_datablob *Purgelist[KOMODO_DEX_MAXPERSEC * KOMODO_DEX_MAXLAG]; - int32_t numpurges; -#endif - FILE *fp; -} *G; - -void komodo_DEX_pubkeyupdate() -{ - komodo_DEX_pubkey(DEX_pubkey); - /*{ - char str[65]; - fprintf(stderr,"new DEX_pubkey %s\n",bits256_str(str,DEX_pubkey)); - }*/ -} - -void komodo_DEX_init() -{ - static int32_t onetime; int32_t modval; - if ( onetime == 0 ) - { - decode_hex(GENESIS_PUBKEY.bytes,sizeof(GENESIS_PUBKEY),GENESIS_PUBKEYSTR); - decode_hex(GENESIS_PRIVKEY.bytes,sizeof(GENESIS_PRIVKEY),GENESIS_PRIVKEYSTR); - pthread_mutex_init(&DEX_globalmutex,0); - komodo_DEX_pubkeyupdate(); - G = (struct DEX_globals *)calloc(1,sizeof(*G)); - if ( (G->fp= fopen((char *)"DEX.log",(char *)"wb")) == 0 ) - { - fprintf(stderr,"FATAL ERROR couldnt open DEX.log file\n"); - exit(-1); - } - char str[67]; fprintf(stderr,"DEX_pubkey.(01%s) sizeof DEX_globals %ld\n\n",bits256_str(str,DEX_pubkey),sizeof(*G)); - onetime = 1; - } -} - -uint32_t komodo_DEX_listid() -{ - static uint32_t listid; - return(++listid); -} - -int32_t komodo_DEX_islagging() -{ - if ( (DEX_lag > DEX_lag2 && DEX_lag2 > DEX_lag3 && DEX_lag > KOMODO_DEX_MAXLAG/KOMODO_DEX_MAXHOPS && DEX_Numpending >= KOMODO_DEX_MAXPERSEC/2) || DEX_Numpending >= KOMODO_DEX_MAXPERSEC ) - return(1); - else return(0); -} - -int32_t komodo_DEX_sizepriority(uint32_t packetsize) -{ - int32_t n,priority = 0; - n = (packetsize >> KOMODO_DEX_TXPOWDIVBITS); - while ( n != 0 ) - { - priority++; - n >>= 1; - } - if ( 0 && priority != 0 ) - fprintf(stderr,"sizepriority.%d from packetsize.%d\n",priority,packetsize); - return(priority); -} - -int32_t komodo_DEX_countbits(uint64_t h) -{ - int32_t i; - for (i=0; i<64; i++,h>>=1) - if ( (h & 1) != 0 ) - return(i); - return(i); -} - -int32_t komodo_DEX_priority(uint64_t h,int32_t packetsize) -{ - int32_t i,sizepriority = komodo_DEX_sizepriority(packetsize); - h >>= KOMODO_DEX_TXPOWBITS; - i = komodo_DEX_countbits(h); - return(i - sizepriority); -} - -uint32_t komodo_DEXquotehash(bits256 &hash,uint8_t *msg,int32_t len) -{ - bits256 tmp,tmp2; - vcalc_sha256(0,tmp.bytes,&msg[1],len-1); - tmp2 = curve25519(tmp,curve25519_basepoint9()); - vcalc_sha256(0,hash.bytes,tmp2.bytes,sizeof(tmp2)); - return(_komodo_DEXquotehash(hash,len)); -} - -uint16_t _komodo_DEXpeerpos(uint32_t timestamp,int32_t peerid) -{ - // this needs to maintain the bitposition from epoch to epoch, to preserve the accuracy of the GETBIT() - int32_t epoch,*peermap; uint16_t i; - if ( G != 0 ) - { - epoch = ((timestamp % SECONDS_IN_DAY) / KOMODO_DEX_PEERPERIOD); - peermap = G->DEX_peermaps[epoch]; - for (i=1; iprevs[ind] = (head)->prevs[ind]; \ -(head)->prevs[ind]->nexts[ind] = (add); \ -(head)->prevs[ind] = (add); \ -(add)->nexts[ind] = NULL; \ -} else { \ -(head)=(add); \ -(head)->prevs[ind] = (head); \ -(head)->nexts[ind] = NULL; \ -} \ -} while (0) - -#define DL_DELETEind(head,del,ind) \ -DL_DELETE2ind(head,del,prevs,nexts,ind) - -#define DL_DELETE2ind(head,del,prevs,nexts,ind) \ -do { \ -assert((del)->prevs[ind] != NULL); \ -if ((del)->prevs[ind] == (del)) { \ -(head)=NULL; \ -} else if ((del)==(head)) { \ -(del)->nexts[ind]->prevs[ind] = (del)->prevs[ind]; \ -(head) = (del)->nexts[ind]; \ -} else { \ -(del)->prevs[ind]->nexts[ind] = (del)->nexts[ind]; \ -if ((del)->nexts[ind]) { \ -(del)->nexts[ind]->prevs[ind] = (del)->prevs[ind]; \ -} else { \ -(head)->prevs[ind] = (del)->prevs[ind]; \ -} \ -} \ -} while (0) - -#define DL_FOREACHind(tail,el,ind) \ -DL_FOREACH2ind(tail,el,prevs,ind) - -#define DL_FOREACH2ind(tail,el,prevs,ind) \ -for(el=tail;el;el=(el)->prevs[ind]) - -void _komodo_DEX_enqueue(int32_t ind,struct DEX_index *index,struct DEX_datablob *ptr) -{ - if ( GETBIT(&ptr->linkmask,ind) != 0 ) - { - fprintf(stderr,"duplicate link attempted ind.%d ptr.%p listid.%d\n",ind,ptr,ptr->lastlist); - return; - } - if ( ptr->datalen < KOMODO_DEX_ROUTESIZE ) - { - fprintf(stderr,"already truncated datablob cant be linked ind.%d ptr.%p listid.%d\n",ind,ptr,ptr->lastlist); - return; - } - DL_APPENDind(index->head,ptr,ind); - index->tail = ptr; - SETBIT(&ptr->linkmask,ind); -} - -uint32_t _komodo_DEXtotal(int32_t *histo,int32_t &total) -{ - struct DEX_datablob *ptr,*tmp; int32_t priority; uint32_t modval,n,hash,totalhash = 0; - total = 0; - for (modval=0; modvalHashtables[modval],ptr,tmp) - { - n++; - hash ^= ptr->shorthash; - if ( (priority= ptr->priority) > 13 ) - priority = 13; - histo[priority]++; - } - totalhash ^= hash; - total += n; - } - return(totalhash); -} - -uint32_t _komodo_DEX_peerclear(int32_t peerpos) -{ - struct DEX_datablob *ptr,*tmp; uint32_t modval,n=0,hash,totalhash = 0; - for (modval=0; modvalHashtables[modval],ptr,tmp) - { - if ( ptr->priority >= KOMODO_DEX_VIPLEVEL ) - { - n++; - CLEARBIT(ptr->peermask,peerpos); - } - } - } - return(n); -} - -int32_t _komodo_DEX_purgeindex(int32_t ind,struct DEX_index *index,uint32_t cutoff) -{ - uint32_t t; int32_t n=0; struct DEX_datablob *ptr = 0; - ptr = index->head; - while ( ptr != 0 ) - { - if ( GETBIT(&ptr->linkmask,ind) == 0 ) - { - fprintf(stderr,"unlink attempted for clearbit ind.%d ptr.%p\n",ind,ptr); - break; - } - iguana_rwnum(0,&ptr->data[2],sizeof(t),&t); - if ( t <= cutoff ) - { - if ( index->tail == index->head ) - index->tail = 0; - DL_DELETEind(index->head,ptr,ind); - n++; - CLEARBIT(&ptr->linkmask,ind); - if ( ptr->linkmask == 0 ) - { -#if KOMODO_DEX_PURGELIST - G->Purgelist[G->numpurges++] = ptr; -#else - free(ptr); - DEX_freed++; -#endif - } // else fprintf(stderr,"%p ind.%d linkmask.%x\n",ptr,ind,ptr->linkmask); - ptr = index->head; - } - else - { - if ( 0 && t > cutoff ) - fprintf(stderr,"purgeindex.%d cutoff %u got future t.%u\n",ind,cutoff,t); - break; - } - } - return(n); -} - -int32_t _komodo_DEXpurge(uint32_t cutoff) -{ - static uint32_t prevtotalhash,lastadd,lastcutoff; - int32_t i,n=0,modval,total,offset,h; int64_t lagsum = 0; uint8_t relay,funcid,*msg; uint32_t t,totalhash,purgehash=0; struct DEX_datablob *ptr,*tmp; - if ( (cutoff % SECONDS_IN_DAY) == (SECONDS_IN_DAY-1) ) - { - fprintf(stderr,"reset peermaps at end of day!\n"); - memset(G->DEX_peermaps,0,sizeof(G->DEX_peermaps)); - } - modval = (cutoff % KOMODO_DEX_PURGETIME); - HASH_ITER(hh,G->Hashtables[modval],ptr,tmp) - { - msg = &ptr->data[0]; - relay = msg[0]; - funcid = msg[1]; - iguana_rwnum(0,&msg[2],sizeof(t),&t); - if ( t <= cutoff ) - { - if ( ptr->recvtime >= t ) - lagsum += (ptr->recvtime - t); - purgehash ^= ptr->shorthash; - HASH_DELETE(hh,G->Hashtables[modval],ptr); - ptr->datalen = 0; - CLEARBIT(&ptr->linkmask,KOMODO_DEX_MAXINDICES); - DEX_truncated++; - n++; - } // else fprintf(stderr,"modval.%d unexpected purge.%d t.%u vs cutoff.%u\n",modval,i,t,cutoff); - } - //totalhash = _komodo_DEXtotal(total); - if ( (modval % 60) == 0 ) // n != 0 || //totalhash != prevtotalhash ) - { - int32_t histo[64]; - memset(histo,0,sizeof(histo)); - totalhash = 0; - total = 0; - if ( (modval % 60) == 0 ) - totalhash = _komodo_DEXtotal(histo,total); - fprintf(stderr,"%d: del.%d %08x, RAM.%d %08x R.%lld S.%lld A.%lld dup.%lld | L.%lld A.%lld coll.%lld | lag %.3f (%.4f %.4f %.4f) err.%lld pend.%lld T/F %lld/%lld | ",modval,n,purgehash,total,totalhash,(long long)DEX_totalrecv,(long long)DEX_totalsent,(long long)DEX_totaladd,(long long)DEX_duplicate,(long long)DEX_lookup32,(long long)DEX_add32,(long long)DEX_collision32,n>0?(double)lagsum/n:0,DEX_lag,DEX_lag2,DEX_lag3,(long long)DEX_maxlag,(long long)DEX_Numpending,(long long)DEX_truncated,(long long)DEX_freed); - for (i=13; i>=0; i--) - fprintf(stderr,"%.0f ",(double)histo[i]);//1000.*histo[i]/(total+1)); // expected 1 1 2 5 | 10 10 10 10 10 | 10 9 9 7 5 - fprintf(stderr,"%s %lld/sec\n",komodo_DEX_islagging()!=0?"LAG":"",cutoff>lastcutoff ? (long long)(DEX_totaladd - lastadd)/(cutoff - lastcutoff) : 0); - if ( cutoff > lastcutoff ) - { - lastadd = DEX_totaladd; - prevtotalhash = totalhash; - lastcutoff = cutoff; - } - } - return(n); -} - -int32_t _komodo_DEX_purgeindices(uint32_t cutoff) -{ - int32_t i,j,n=0; uint32_t t; struct DEX_datablob *ptr; struct DEX_index *index = 0,*tmp; - if ( DEX_destpubs != 0 ) - { - HASH_ITER(hh,DEX_destpubs,index,tmp) - { - n += _komodo_DEX_purgeindex(0,index,cutoff); - } - } - if ( DEX_tagAs != 0 ) - { - HASH_ITER(hh,DEX_tagAs,index,tmp) - { - n += _komodo_DEX_purgeindex(1,index,cutoff); - } - } - if ( DEX_tagBs != 0 ) - { - HASH_ITER(hh,DEX_tagBs,index,tmp) - { - n += _komodo_DEX_purgeindex(2,index,cutoff); - } - } - if ( DEX_tagABs != 0 ) - { - HASH_ITER(hh,DEX_tagABs,index,tmp) - { - n += _komodo_DEX_purgeindex(3,index,cutoff); - } - } -#if KOMODO_DEX_PURGELIST - for (i=0; inumpurges; i++) - { - if ( (ptr= G->Purgelist[i]) != 0 ) - { - iguana_rwnum(0,&ptr->data[2],sizeof(t),&t); - if ( t <= cutoff - KOMODO_DEX_MAXLAG/2 ) - { - if ( ptr->linkmask == 0 ) - { - G->Purgelist[i] = G->Purgelist[--G->numpurges]; - G->Purgelist[G->numpurges] = 0; - i--; - free(ptr); - DEX_freed++; - } else fprintf(stderr,"ptr is still accessed? linkmask.%x\n",ptr->linkmask); - } - } else fprintf(stderr,"unexpected null ptr at %d of %d\n",i,G->numpurges); - } -#endif - return(n); -} - -char *komodo_DEX_keystr(char *str,uint8_t *key,int8_t keylen) -{ - int32_t i; - str[0] = 0; - if ( keylen == 34 ) - { - for (i=0; i<33; i++) - sprintf(&str[i<<1],"%02x",key[i+1]); - str[i<<1] = 0; - } - else - { - if ( key[0] == keylen-1 ) - { - memcpy(str,&key[1],key[0]); - str[key[0]] = 0; - } - else if ( key[0]+key[key[0]+1]+2 == keylen ) - { - memcpy(str,&key[1],key[0]); - str[key[0]] = '/'; - memcpy(&str[key[0]+1],&key[key[0]+2],key[key[0]+1]); - str[key[0]+1+key[key[0]+1]] = 0; - } - else if ( keylen != 0 ) - fprintf(stderr,"strange keylen %d vs [%d %d]\n",keylen,key[0],key[key[0]+1]); - } - return(str); -} - -struct DEX_index *_komodo_DEX_indexfind(int32_t ind,uint8_t *keybuf,int32_t keylen) -{ - struct DEX_index *index = 0; - switch ( ind ) - { - case 0: HASH_FIND(hh,DEX_destpubs,keybuf,keylen,index); break; - case 1: HASH_FIND(hh,DEX_tagAs,keybuf,keylen,index); break; - case 2: HASH_FIND(hh,DEX_tagBs,keybuf,keylen,index); break; - case 3: HASH_FIND(hh,DEX_tagABs,keybuf,keylen,index); break; - } - return(index); -} - -struct DEX_index *_komodo_DEX_indexcreate(int32_t ind,uint8_t *key,int8_t keylen,struct DEX_datablob *ptr) -{ - struct DEX_index *index = (struct DEX_index *)calloc(1,sizeof(*index)); - if ( index == 0 ) - { - fprintf(stderr,"out of memory\n"); - return(0); - } - memcpy(index->key,key,keylen); - if ( 0 ) - { - int32_t i; - for (i=0; ikeylen = keylen; - switch ( ind ) - { - case 0: HASH_ADD_KEYPTR(hh,DEX_destpubs,index->key,index->keylen,index); break; - case 1: HASH_ADD_KEYPTR(hh,DEX_tagAs,index->key,index->keylen,index); break; - case 2: HASH_ADD_KEYPTR(hh,DEX_tagBs,index->key,index->keylen,index); break; - case 3: HASH_ADD_KEYPTR(hh,DEX_tagABs,index->key,index->keylen,index); break; - } - _komodo_DEX_enqueue(ind,index,ptr); - return(index); -} - -struct DEX_index *_DEX_indexsearch(int32_t ind,int32_t priority,struct DEX_datablob *ptr,int8_t lenA,uint8_t *key,int8_t lenB,uint8_t *tagB) -{ - uint8_t keybuf[KOMODO_DEX_MAXKEYSIZE]; int32_t i,keylen = 0; struct DEX_index *index = 0; - if ( lenA == 33 ) - { - keylen = 34; - keybuf[0] = 33; - memcpy(&keybuf[1],key,33); - if ( ind != 0 ) - fprintf(stderr,"expected ind.0 instead of %d\n",ind); - //index = DEX_destpubs; - } - else if ( lenB == 0 ) - { - keylen = lenA+1; - keybuf[0] = lenA; - memcpy(&keybuf[1],key,lenA); - if ( ind != 1 ) - fprintf(stderr,"expected ind.1 instead of %d\n",ind); - //index = DEX_tagAs; - } - else if ( lenA == 0 ) - { - keylen = lenB+1; - keybuf[0] = lenB; - memcpy(&keybuf[1],tagB,lenB); - if ( ind != 2 ) - fprintf(stderr,"expected ind.2 instead of %d\n",ind); - //index = DEX_tagBs; - } - else if ( lenA > 0 && lenB > 0 && tagB != 0 && lenA <= KOMODO_DEX_TAGSIZE && lenB <= KOMODO_DEX_TAGSIZE ) - { - keybuf[keylen++] = lenA; - memcpy(&keybuf[keylen],key,lenA), keylen += lenA; - keybuf[keylen++] = lenB; - memcpy(&keybuf[keylen],tagB,lenB), keylen += lenB; - if ( ind != 3 ) - fprintf(stderr,"expected ind.3 instead of %d\n",ind); - //index = DEX_tagABs; - } - if ( (index= _komodo_DEX_indexfind(ind,keybuf,keylen)) != 0 ) - { - if ( ptr != 0 ) - _komodo_DEX_enqueue(ind,index,ptr); - return(index); - } - if ( ptr == 0 ) - return(0); - /*if ( priority < KOMODO_DEX_CREATEINDEX_MINPRIORITY ) the category creation needs to be in OP_RETURN, could use the same format data packet - { - fprintf(stderr,"new index lenA.%d lenB.%d, insufficient priority.%d < minpriority.%d to create\n",lenA,lenB,priority,KOMODO_DEX_CREATEINDEX_MINPRIORITY); - return(0); - } - }*/ - return(_komodo_DEX_indexcreate(ind,keybuf,keylen,ptr)); -} - -int32_t _DEX_updatetips(struct DEX_index *tips[KOMODO_DEX_MAXINDICES],int32_t priority,struct DEX_datablob *ptr,int8_t lenA,uint8_t *tagA,int8_t lenB,uint8_t *tagB,uint8_t *destpub,int8_t plen) -{ - int32_t ind = 0,mask = 0; - memset(tips,0,sizeof(*tips) * KOMODO_DEX_MAXINDICES); - if ( lenA == 0 && lenB == 0 && plen == 0 ) - return(0); - if ( plen == 33 ) - { - if ( (tips[ind]= _DEX_indexsearch(ind,priority,ptr,plen,destpub,0,0)) == 0 ) - mask |= (1 << (ind+16)); - else mask |= (1 << ind); - } - ind++; - if ( lenA != 0 ) - { - if ( (tips[ind]= _DEX_indexsearch(ind,priority,ptr,lenA,tagA,0,0)) == 0 ) - mask |= (1 << (ind+16)); - else mask |= (1 << ind); - ind++; - if ( lenB != 0 ) - { - if ( (tips[ind]= _DEX_indexsearch(ind,priority,ptr,0,0,lenB,tagB)) == 0 ) - mask |= (1 << (ind+16)); - else mask |= (1 << ind); - ind++; - if ( (tips[ind]= _DEX_indexsearch(ind,priority,ptr,lenA,tagA,lenB,tagB)) == 0 ) - mask |= (1 << (ind+16)); - else mask |= (1 << ind); - } - } - else if ( lenB != 0 ) - { - ind++; - if ( (tips[ind]= _DEX_indexsearch(ind,priority,ptr,0,0,lenB,tagB)) == 0 ) // must have same ind as lenA case above! - mask |= (1 << (ind+16)); - else mask |= (1 << ind); - } - if ( ind >= KOMODO_DEX_MAXINDICES ) - { - fprintf(stderr,"DEX_updatetips: impossible case ind.%d > KOMODO_DEX_MAXINDICES %d\n",ind,KOMODO_DEX_MAXINDICES); - exit(1); - } - if ( ptr != 0 ) - { - //fprintf(G->fp,"tips updated %x ptr.%p\n",mask,ptr); - //fflush(G->fp); - } - return(mask); // err bits are <<= 16 -} - -int32_t komodo_DEX_extract(uint64_t &amountA,uint64_t &amountB,int8_t &lenA,uint8_t tagA[KOMODO_DEX_TAGSIZE],int8_t &lenB,uint8_t tagB[KOMODO_DEX_TAGSIZE],uint8_t destpub33[33],int8_t &plen,uint8_t *msg,int32_t len) -{ - int32_t offset = 0; - if ( len < sizeof(amountA)+sizeof(amountB)+3 ) - return(-1); - memset(destpub33,0,33); - offset = iguana_rwnum(0,&msg[offset],sizeof(amountA),&amountA); - offset += iguana_rwnum(0,&msg[offset],sizeof(amountB),&amountB); - if ( (plen= msg[offset++]) != 0 ) - { - if ( plen == 33 ) - { - memcpy(destpub33,&msg[offset],33); - offset += 33; - } - else - { - fprintf(stderr,"reject invalid plen.%d\n",plen); - return(-1); - } - } - if ( (lenA= msg[offset++]) != 0 ) - { - if ( lenA <= KOMODO_DEX_TAGSIZE ) - { - memcpy(tagA,&msg[offset],lenA); - offset += lenA; - } - else - { - fprintf(stderr,"reject invalid lenA.%d\n",lenA); - return(-1); - } - } - if ( (lenB= msg[offset++]) != 0 ) - { - if ( lenB <= KOMODO_DEX_TAGSIZE ) - { - memcpy(tagB,&msg[offset],lenB); - offset += lenB; - } - else - { - fprintf(stderr,"reject invalid lenB.%d\n",lenB); - return(-1); - } - } - return(offset); -} - -int32_t komodo_DEX_tagsextract(uint64_t &amountA,uint64_t &amountB,char taga[],char tagb[],char pubkeystr[],uint8_t destpub33[33],struct DEX_datablob *ptr) -{ - uint8_t *msg,tagA[KOMODO_DEX_TAGSIZE+1],tagB[KOMODO_DEX_TAGSIZE+1]; int8_t lenA,lenB,plen; int32_t i,offset,len; - msg = &ptr->data[KOMODO_DEX_ROUTESIZE]; - len = ptr->datalen-KOMODO_DEX_ROUTESIZE; - taga[0] = tagb[0] = 0; - if ( pubkeystr != 0 ) - pubkeystr[0] = 0; - amountA = amountB = 0; - memset(destpub33,0,33); - memset(tagA,0,sizeof(tagA)); - memset(tagB,0,sizeof(tagB)); - if ( (offset= komodo_DEX_extract(amountA,amountB,lenA,tagA,lenB,tagB,destpub33,plen,msg,len)) < 0 ) - return(-1); - if ( lenA != 0 ) - strcpy(taga,(char *)tagA); - if ( lenB != 0 ) - strcpy(tagb,(char *)tagB); - if ( plen == 33 && pubkeystr != 0 ) - { - for (i=0; i<33; i++) - sprintf(&pubkeystr[i<<1],"%02x",destpub33[i]); - pubkeystr[i<<1] = 0; - } - return(0); -} - -struct DEX_datablob *_komodo_DEXfind(int32_t modval,uint32_t shorthash) -{ - uint32_t hashval; int32_t i,hashind; struct DEX_datablob *ptr; - if ( modval < 0 || modval >= KOMODO_DEX_PURGETIME ) - { - fprintf(stderr,"DEXfind illegal modval.%d\n",modval); - return(0); - } - HASH_FIND(hh,G->Hashtables[modval],&shorthash,sizeof(shorthash),ptr); - return(ptr); -} - -struct DEX_datablob *_komodo_DEXadd(uint32_t now,int32_t modval,bits256 hash,uint32_t shorthash,uint8_t *msg,int32_t len) -{ - int32_t ind,offset,priority; struct DEX_datablob *ptr; struct DEX_index *tips[KOMODO_DEX_MAXINDICES]; uint64_t amountA,amountB; uint8_t tagA[KOMODO_DEX_TAGSIZE+1],tagB[KOMODO_DEX_TAGSIZE+1],destpub33[33]; int8_t lenA,lenB,plen; - if ( modval < 0 || modval >= KOMODO_DEX_PURGETIME ) - { - fprintf(stderr,"komodo_DEXadd illegal modval.%d\n",modval); - return(0); - } - if ( (hash.ulongs[0] & KOMODO_DEX_TXPOWMASK) != (0x777 & KOMODO_DEX_TXPOWMASK) ) - { - static uint32_t count; char str[65]; - if ( count++ < 10 ) - fprintf(stderr," reject quote due to invalid hash[1] %016llx %s\n",(long long)hash.ulongs[0],bits256_str(str,hash)); - return(0); - } else priority = komodo_DEX_priority(hash.ulongs[0],len); - if ( (ptr= _komodo_DEXfind(modval,shorthash)) != 0 ) - return(ptr); - memset(tagA,0,sizeof(tagA)); - memset(tagB,0,sizeof(tagB)); - if ( (offset= komodo_DEX_extract(amountA,amountB,lenA,tagA,lenB,tagB,destpub33,plen,&msg[KOMODO_DEX_ROUTESIZE],len-KOMODO_DEX_ROUTESIZE)) < 0 ) - return(0); - if ( (ptr= (struct DEX_datablob *)calloc(1,sizeof(*ptr) + len)) != 0 ) - { - ptr->recvtime = now; - ptr->hash = hash; - ptr->shorthash = shorthash; - ptr->datalen = len; - ptr->priority = priority; - ptr->sizepriority = komodo_DEX_sizepriority(len); - ptr->offset = offset + KOMODO_DEX_ROUTESIZE; // payload is after relaydepth, funcid, timestamp - memcpy(ptr->data,msg,len); - ptr->data[0] = msg[0] != 0xff ? msg[0] - 1 : msg[0]; - { - HASH_ADD(hh,G->Hashtables[modval],shorthash,sizeof(ptr->shorthash),ptr); - SETBIT(&ptr->linkmask,KOMODO_DEX_MAXINDICES); - DEX_totaladd++; - if ( (_DEX_updatetips(tips,priority,ptr,lenA,tagA,lenB,tagB,destpub33,plen) >> 16) != 0 ) - fprintf(stderr,"update M.%d slot.%d [%d] with %08x error updating tips\n",modval,ind,ptr->data[0],ptr->shorthash); - } - return(ptr); - } - fprintf(stderr,"out of memory\n"); - return(0); -} - -int32_t komodo_DEXgenget(std::vector &getshorthash,uint32_t timestamp,uint32_t shorthash,int32_t modval) -{ - int32_t len = 0; - getshorthash.resize(2 + 2*sizeof(uint32_t) + sizeof(modval)); - getshorthash[len++] = 0; - getshorthash[len++] = 'G'; - len += iguana_rwnum(1,&getshorthash[len],sizeof(timestamp),×tamp); - len += iguana_rwnum(1,&getshorthash[len],sizeof(shorthash),&shorthash); - len += iguana_rwnum(1,&getshorthash[len],sizeof(modval),&modval); - return(len); -} - -int32_t komodo_DEXgenping(uint8_t funcid,std::vector &ping,uint32_t timestamp,int32_t modval,uint32_t *recents,uint16_t n) -{ - int32_t i,len = 0; - if ( modval < 0 || modval >= KOMODO_DEX_PURGETIME ) - { - fprintf(stderr,"komodo_DEXgenping illegal modval.%d\n",modval); - return(-1); - } - ping.resize(2 + sizeof(timestamp) + sizeof(n) + sizeof(modval) + n*sizeof(uint32_t)); - ping[len++] = 0; - ping[len++] = funcid; - len += iguana_rwnum(1,&ping[len],sizeof(timestamp),×tamp); - len += iguana_rwnum(1,&ping[len],sizeof(n),&n); - len += iguana_rwnum(1,&ping[len],sizeof(modval),&modval); - for (i=0; i "e,uint32_t timestamp,uint8_t hdr[],int32_t hdrlen,uint8_t data[],int32_t datalen) -{ - int32_t i,j,len = 0; uint64_t h; uint32_t nonce = rand(); - quote.resize(2 + sizeof(uint32_t) + hdrlen + datalen + sizeof(nonce)); // send list of recently added shorthashes - quote[len++] = KOMODO_DEX_RELAYDEPTH; - quote[len++] = funcid; - len += iguana_rwnum(1,"e[len],sizeof(timestamp),×tamp); - for (i=0; i>= KOMODO_DEX_TXPOWBITS; - for (j=0; j>=1) - if ( (h & 1) != 0 ) - break; - if ( j < priority ) - { - //fprintf(stderr,"i.%u j.%d failed priority.%d ulongs[0] %016llx\n",i,j,priority,(long long)hash.ulongs[0]); - continue; - } - if ( i > 10000000 ) - fprintf(stderr,"nonce calc: i.%u j.%d priority.%d ulongs[0] %016llx\n",i,j,priority,(long long)hash.ulongs[0]); - break; - } - } -#else - iguana_rwnum(1,"e[len - sizeof(nonce)],sizeof(nonce),&nonce); - shorthash = komodo_DEXquotehash(hash,"e[0],len); -#endif - return(len); -} - -int32_t komodo_DEXpacketsend(CNode *peer,uint8_t peerpos,struct DEX_datablob *ptr,uint8_t resp0) -{ - std::vector packet; int32_t i; - //fprintf(stderr,"%d packet send %p datalen.%d\n",peer->id,ptr,ptr->datalen); - if ( ptr->datalen < KOMODO_DEX_ROUTESIZE || ptr->datalen > KOMODO_DEX_MAXPACKETSIZE ) - { - fprintf(stderr,"illegal datalen.%d\n",ptr->datalen); - return(-1); - } - packet.resize(ptr->datalen); - memcpy(&packet[0],ptr->data,ptr->datalen); - packet[0] = resp0; - peer->PushMessage("DEX",packet); - DEX_totalsent++; - return(ptr->datalen); -} - -int32_t _komodo_DEXmodval(uint32_t now,const int32_t modval,CNode *peer) -{ - static uint32_t recents[16][KOMODO_DEX_MAXPERSEC],sendbuf[KOMODO_DEX_MAXPING]; - std::vector packet; int32_t i,j,n=0,mult,p,vip=0,maxp=0,sum=0; uint16_t peerpos,num[16]; uint8_t priority,relay,funcid,*msg; uint32_t t,h; struct DEX_datablob *ptr=0,*tmp; - if ( modval < 0 || modval >= KOMODO_DEX_PURGETIME || (peerpos= _komodo_DEXpeerpos(now,peer->id)) == 0xffff ) - return(-1); - memset(num,0,sizeof(num)); - HASH_ITER(hh,G->Hashtables[modval],ptr,tmp) - { - n++; - h = ptr->shorthash; - if ( ptr->datalen >= KOMODO_DEX_ROUTESIZE && ptr->datalen < KOMODO_DEX_MAXPACKETSIZE ) - { - msg = &ptr->data[0]; - relay = msg[0]; - funcid = msg[1]; - iguana_rwnum(0,&msg[2],sizeof(t),&t); - if ( now < t+KOMODO_DEX_MAXLAG || ptr->priority >= KOMODO_DEX_VIPLEVEL || ptr->requested > 0 ) //|| now < ptr->recvtime+KOMODO_DEX_MAXHOPS/2+1 ) - { - if ( GETBIT(ptr->peermask,peerpos) == 0 || ptr->requested > 0 ) - { - if ( (p= ptr->priority) >= 16 ) - p = 15; - if ( p < 0 ) - { - fprintf(stderr,"unexpected negative priority.%d\n",p); - continue; - } - if ( p > maxp ) - maxp = p; - if ( num[p] >= (int32_t)(sizeof(recents[p])/sizeof(*recents[p])) ) - { - fprintf(stderr,"num[%d] %d is full\n",p,num[p]); - continue; - } - /*if ( GETBIT(ptr->peermask,peerpos) == 0 && ptr->priority >= KOMODO_DEX_VIPLEVEL ) - { - //fprintf(G->fp,"%08x ",ptr->shorthash); - vip++; - }*/ - recents[p][num[p]++] = h; - if ( ptr->requested > 0 ) - { - //fprintf(G->fp,"%08x.R%d.%d ",ptr->shorthash,ptr->requested,GETBIT(ptr->peermask,peerpos)); - ptr->requested--; - vip++; - } - if ( ptr->numsent < KOMODO_DEX_MAXFANOUT ) - { - if ( (relay >= 0 && relay <= KOMODO_DEX_RELAYDEPTH && now < t+KOMODO_DEX_LOCALHEARTBEAT) ) - { - if ( komodo_DEX_islagging() == 0 ) - { - komodo_DEXpacketsend(peer,peerpos,ptr,ptr->data[0]); - ptr->numsent++; - } - } - } - } - } - } else fprintf(stderr,"ptr.%p %08x with illegal size %d\n",ptr,ptr->shorthash,ptr->datalen); - } - if ( vip != 0 ) - { - //fprintf(G->fp,"missing vip.%d dexmodval.%d peer.%d n.%d\n",vip,modval,peerpos,n); - //fflush(G->fp); - } - for (p=15; p>=0; p--) - { - if ( num[p] != 0 ) - { - if ( (mult= num[p]/KOMODO_DEX_MAXPING) > 1 ) // reduce conflict with pings from other peers - { - i = (rand() % mult); - for (n=0; i 0 ) - peer->PushMessage("DEX",packet); - sum += n; - } - else - { - if ( komodo_DEXgenping('P',packet,now,modval,recents[p],num[p]) > 0 ) - peer->PushMessage("DEX",packet); - sum += num[p]; - } - if ( komodo_DEX_islagging() != 0 ) - return(sum); - } - } - return(sum); -} - -uint8_t *komodo_DEX_datablobdecrypt(bits256 *senderpub,uint8_t **allocatedp,int32_t *newlenp,struct DEX_datablob *ptr,bits256 pubkey,char *taga) -{ - static bits256 zero; - bits256 priv0; uint8_t *decoded; char str[65],str2[65]; - *allocatedp = 0; - *newlenp = 0; - memset(priv0.bytes,0,sizeof(priv0)); - memset(senderpub,0,sizeof(*senderpub)); - //if ( bits256_nonz(pubkey) != 0 ) - { - if ( memcmp(pubkey.bytes,DEX_pubkey.bytes,32) == 0 && strcmp(taga,"inbox") == 0 ) - komodo_DEX_privkey(priv0); - else priv0 = GENESIS_PRIVKEY; - *newlenp = ptr->datalen - 4 - ptr->offset; - if ( (decoded= komodo_DEX_decrypt(senderpub->bytes,allocatedp,&ptr->data[ptr->offset],newlenp,priv0)) != 0 ) - { - if ( memcmp(&priv0,&GENESIS_PRIVKEY,32) == 0 && memcmp(senderpub,&pubkey,32) != 0 && memcmp(pubkey.bytes,zero.bytes,sizeof(zero)) != 0 && memcmp(pubkey.bytes,&GENESIS_PUBKEY,32) != 0 ) - { - fprintf(stderr,"senderpub %s != pubkey %s\n",bits256_str(str,*senderpub),bits256_str(str2,pubkey)); - *newlenp = -1; - decoded = 0; - } - } //else fprintf(stderr,"decrypt error senderpub.%s\n",bits256_str(str,*senderpub)); - memset(priv0.bytes,0,sizeof(priv0)); - } - return(decoded); -} - -int32_t _komodo_DEX_decryptdata(uint8_t *data,int32_t maxlen,struct DEX_datablob *ptr) -{ - uint8_t *decoded,*allocated=0; bits256 zero,senderpub; int32_t newlen = 0; - memset(zero.bytes,0,sizeof(zero)); - if ( (decoded= komodo_DEX_datablobdecrypt(&senderpub,&allocated,&newlen,ptr,zero,(char *)"")) != 0 ) - { - if ( newlen < maxlen ) - memcpy(data,decoded,newlen); - else newlen = -1; - } - if ( allocated != 0 ) - free(allocated); - return(newlen); -} - -int32_t komodo_DEX_cancelupdate(struct DEX_datablob *ptr,char *tagA,char *tagB,bits256 senderpub,uint32_t cutoff) -{ - uint64_t amountA,amountB; char taga[KOMODO_DEX_MAXKEYSIZE+1],tagb[KOMODO_DEX_MAXKEYSIZE+1]; uint8_t pubkey33[33]; - if ( komodo_DEX_tagsextract(amountA,amountB,taga,tagb,0,pubkey33,ptr) < 0 ) - return(-2); - if ( pubkey33[0] != 0x01 || memcmp(pubkey33+1,senderpub.bytes,32) != 0 ) - { - fprintf(stderr,"illegal trying to cancel another pubkey datablob! banscore this\n"); - return(-3); - } - if ( tagA[0] != 0 && strcmp(tagA,taga) != 0 ) - { - //fprintf(stderr,"tagA %s mismatch %s\n",tagA,taga); - return(-4); - } - if ( tagB[0] != 0 && strcmp(tagB,tagb) != 0 ) - { - //fprintf(stderr,"tagB %s mismatch %s\n",tagB,tagb); - return(-5); - } - if ( ptr->cancelled != 0 ) - return(0); - else - { - ptr->cancelled = cutoff; - //fprintf(stderr,"(%08x) cancel at %u\n",ptr->shorthash,ptr->cancelled); - return(1); - } -} - -int32_t _komodo_DEX_cancelid(uint32_t shorthash,bits256 senderpub,uint32_t cutoff) -{ - int32_t modval; struct DEX_datablob *ptr; - for (modval=0; modvaltail; ptr!=0; ptr=ptr->prevs[ind]) - { - iguana_rwnum(0,&ptr->data[2],sizeof(t),&t); - if ( t < cutoff-KOMODO_DEX_MAXHOPS && komodo_DEX_cancelupdate(ptr,tagA,tagB,senderpub,cutoff) >= 0 ) - { - //fprintf(stderr,"cancel ptr.%p %08x t.%u cutoff.%u (%s,%s) cancelled.%u\n",ptr,ptr->shorthash,t,cutoff-KOMODO_DEX_MAXHOPS,tagA,tagB,ptr->cancelled); - n++; - } - if ( ptr == index->head ) - break; - } - } - return(n); -} - -int32_t _komodo_DEX_locatorsextract(int32_t sendflag,uint32_t shorthash,int32_t modval,int32_t priority) -{ - static bits256 zero; - uint8_t *allocated=0,*decoded; int32_t i,j,n=0,numrequests,newlen=0; bits256 senderpub; uint64_t locator; struct DEX_datablob *refptr,*ptr; - if ( (refptr= _komodo_DEXfind(modval,shorthash)) == 0 ) - return(-1); - if ( (decoded= komodo_DEX_datablobdecrypt(&senderpub,&allocated,&newlen,refptr,zero,(char *)"")) != 0 && (newlen & 7) == 0 ) - { - numrequests = 50 + priority * 10; - if ( numrequests > 250 ) - numrequests = 250; - for (i=sizeof(uint64_t),j=0; i> 32) % KOMODO_DEX_PURGETIME,(uint32_t)locator)) != 0 ) - { - ptr->requested = numrequests; - //fprintf(stderr,"%u ",ptr->shorthash); - n++; - } - else if ( sendflag != 0 ) - { - // send request to peer - } - } - } - if ( allocated != 0 ) - free(allocated); - //if ( n > 0 ) - // fprintf(stderr," set %d ptrs requests.%d\n",n,numrequests); - return(n); -} - -int32_t _komodo_DEX_commandprocessor(struct DEX_datablob *ptr,int32_t addedflag,int32_t peerpos) -{ - char _taga[KOMODO_DEX_MAXKEYSIZE+1],_tagb[KOMODO_DEX_MAXKEYSIZE+1],taga[KOMODO_DEX_MAXKEYSIZE+1],tagb[KOMODO_DEX_MAXKEYSIZE+1],str[65]; uint8_t pubkey33[33],*decoded,*allocated; bits256 pubkey,senderpub; uint32_t t,shorthash; int32_t modval,lenA,lenB,n,newlen=0; uint64_t amountA,amountB; - if ( ptr->priority < KOMODO_DEX_CMDPRIORITY ) - return(-1); - if ( komodo_DEX_tagsextract(amountA,amountB,taga,tagb,0,pubkey33,ptr) < 0 ) - return(-2); - if ( pubkey33[0] != 0x01 ) - return(-3); - memcpy(pubkey.bytes,pubkey33+1,32); - //if ( memcmp(pubkey.bytes,DEX_pubkey.bytes,32) == 0 ) - // addedflag = 1; - if ( addedflag != 0 ) - { - if ( (decoded= komodo_DEX_datablobdecrypt(&senderpub,&allocated,&newlen,ptr,pubkey,taga)) != 0 && newlen > 0 ) - { - if ( ptr->data[1] == 'R' ) - { - if ( newlen == sizeof(uint32_t)*2 ) - { - iguana_rwnum(0,decoded,sizeof(shorthash),&shorthash); - iguana_rwnum(0,decoded+sizeof(uint32_t),sizeof(t),&t); - /*if ( shorthash == 0xffffffff && t == 0xffffffff ) - n = _komodo_DEX_peerclear(peerpos); - else*/ n = _komodo_DEX_locatorsextract(0,shorthash,t % KOMODO_DEX_PURGETIME,ptr->priority); - fprintf(stderr,"received REQUEST command for (%s/%s) %08x t.%u -> updated %d ptrs\n",taga,tagb,shorthash,t,n); - } else fprintf(stderr,"newlen.%d != 8 for 'R'\n",newlen); - } - else if ( ptr->data[1] == 'A' ) - { - //fprintf(stderr,"got anonsend newlen.%d\n",newlen); - } - else if ( ptr->data[1] == 'X' ) - { - if ( strcmp(taga,"cancel") != 0 ) - fprintf(stderr,"expected tagA cancel, but got (%s)\n",taga); - else - { - iguana_rwnum(0,&ptr->data[2],sizeof(t),&t); - //fprintf(stderr,"funcid.%c decoded %d bytes tagA.(%s)\n",ptr->data[1],newlen,taga); - if ( newlen == 4 ) - { - iguana_rwnum(0,decoded,sizeof(shorthash),&shorthash); - _komodo_DEX_cancelid(shorthash,senderpub,t); - } - else if ( newlen == 33 && decoded[0] == 0x01 ) // depends on pubkey format - { - if ( memcmp(&decoded[1],senderpub.bytes,32) == 0 ) - { - _komodo_DEX_cancelpubkey((char *)"",(char *)"",decoded,t); - } else fprintf(stderr,"unexpected payload mismatch senderpub\n"); - } - else if ( newlen < KOMODO_DEX_MAXKEYSIZE ) - { - lenA = decoded[0]; - lenB = decoded[lenA+1]; - if ( 0 ) - { - int32_t i; - for (i=0; idata[1],ptr->data[1]); - } else fprintf(stderr,"decode error, newlen.%d\n",newlen); - if ( allocated != 0 ) - free(allocated); - } - return(newlen); -} - -int32_t _komodo_DEXprocess(uint32_t now,CNode *pfrom,uint8_t *msg,int32_t len) -{ - static uint32_t cache[2],pongbuf[KOMODO_DEX_MAXPING]; - int32_t i,j,ind,m,p,tmpval,haves,offset,flag,modval,lag,priority,addedflag=0; uint16_t n,peerpos; uint32_t t,h; uint8_t funcid,relay=0; bits256 hash; struct DEX_datablob *ptr; - peerpos = _komodo_DEXpeerpos(now,pfrom->id); - //fprintf(stderr,"peer.%d msg[%d] %c\n",peerpos,len,msg[1]); - if ( len > KOMODO_DEX_ROUTESIZE+sizeof(uint32_t) && peerpos != 0xffff && len < KOMODO_DEX_MAXPACKETSIZE ) - { - relay = msg[0]; - funcid = msg[1]; - iguana_rwnum(0,&msg[2],sizeof(t),&t); - modval = (t % KOMODO_DEX_PURGETIME); - lag = (now - t); - if ( lag < 0 ) - lag = 0; - h = komodo_DEXquotehash(hash,msg,len); - priority = komodo_DEX_priority(hash.ulongs[0],len); - if ( t > now+KOMODO_DEX_LOCALHEARTBEAT ) - { - fprintf(stderr,"reject packet from future t.%u vs now.%u\n",t,now); - } - /*else if ( lag >= KOMODO_DEX_MAXLAG && priority < KOMODO_DEX_VIPLEVEL ) - { - DEX_maxlag++; - //fprintf(stderr,"reject packet with too big lag t.%u vs now.%u lag.%d\n",t,now,lag); - }*/ - else if ( funcid == 'Q' || funcid == 'X' || funcid == 'R' || funcid == 'A' ) - { - DEX_totalrecv++; - //fprintf(stderr," f.%c t.%u [%d] ",funcid,t,relay); - //fprintf(G->fp," recv modval.%d from (%s) relay.%d p.%d shorthash.%08x %016llx\n",modval,pfrom->addr.ToString().c_str(),relay,priority,h,(long long)hash.ulongs[0]); - if ( (hash.ulongs[0] & KOMODO_DEX_TXPOWMASK) != (0x777 & KOMODO_DEX_TXPOWMASK) ) - { - static uint32_t count; - if ( count++ < 10 ) - fprintf(stderr,"reject quote due to invalid hash[0] %016llx\n",(long long)hash.ulongs[0]); - } - else if ( priority < 0 ) - { - static uint32_t count; - if ( count++ < 10 ) - fprintf(stderr,"reject quote due to insufficient priority.%d for size.%d, needed %d\n",komodo_DEX_priority(hash.ulongs[0],0),len,komodo_DEX_sizepriority(len)); - - } - else if ( relay <= KOMODO_DEX_RELAYDEPTH || relay == 0xff ) - { - if ( (ptr= _komodo_DEXfind(modval,h)) == 0 ) - { - if ( (ptr= _komodo_DEXadd(now,modval,hash,h,msg,len)) != 0 ) - { - addedflag = 1; - if ( komodo_DEXfind32(G->Pendings,(int32_t)(sizeof(G->Pendings)/sizeof(*G->Pendings)),h,1) >= 0 ) - { - if ( DEX_Numpending > 0 ) - DEX_Numpending--; - } - Got_Recent_Quote = now; - if ( lag > 0 && lag < KOMODO_DEX_MAXLAG ) - { - if ( DEX_lag == 0. ) - DEX_lag = DEX_lag2 = DEX_lag3 = lag; - else - { - DEX_lag = (DEX_lag * 0.999) + (0.001 * lag); - DEX_lag2 = (DEX_lag2 * 0.9999) + (0.0001 * lag); - DEX_lag3 = (DEX_lag3 * 0.99999) + (0.00001 * lag); - } - } - if ( cache[0] != now ) - { - cache[0] = now; - cache[1] = ptr->priority; - } - else if ( ptr->priority > cache[1] ) - cache[1] = ptr->priority; - } else fprintf(stderr,"error adding %08x\n",h); - } - else - { - DEX_duplicate++; - } - if ( ptr != 0 ) - { - SETBIT(ptr->peermask,peerpos); - if ( funcid != 'Q' ) - _komodo_DEX_commandprocessor(ptr,addedflag,peerpos); - } - } else fprintf(stderr,"unexpected relay.%d\n",relay); - } - else if ( funcid == 'P' || funcid == 'p' ) - { - std::vector getshorthash; - haves = 0; - if ( len >= 12 && len < KOMODO_DEX_MAXPING*sizeof(uint32_t)+64 ) - { - offset = KOMODO_DEX_ROUTESIZE; - offset += iguana_rwnum(0,&msg[offset],sizeof(n),&n); - offset += iguana_rwnum(0,&msg[offset],sizeof(m),&m); - if ( offset+n*sizeof(uint32_t) == len && m >= 0 && m < KOMODO_DEX_PURGETIME ) - { - for (flag=i=0; ipeermask,peerpos); - pongbuf[haves++] = h; - continue; - } - if ( funcid == 'p' || DEX_Numpending > KOMODO_DEX_MAXPERSEC ) - continue; - p = komodo_DEX_countbits(h); - if ( p < KOMODO_DEX_VIPLEVEL && komodo_DEX_islagging() != 0 && p < cache[1] ) // adjusts for txpowbits and sizebits - { - //fprintf(stderr,"skip estimated priority.%d with cache[%u %d]\n",komodo_DEX_countbits(h),cache[0],cache[1]); - continue; - } - tmpval = komodo_DEXfind32(G->Pendings,(int32_t)(sizeof(G->Pendings)/sizeof(*G->Pendings)),h,0); - if ( tmpval < 0 ) //|| (p >= KOMODO_DEX_VIPLEVEL && (rand() % 10)) ) - { - komodo_DEXadd32(G->Pendings,(int32_t)(sizeof(G->Pendings)/sizeof(*G->Pendings)),h); - //fprintf(G->fp,">>>> %d/%08x <<<<< ",m,h); - if ( tmpval < 0 ) - DEX_Numpending++; - komodo_DEXgenget(getshorthash,now,h,m); - pfrom->PushMessage("DEX",getshorthash); - flag++; - } - //fprintf(G->fp,"%d/%08x ",m,h); - } - if ( (0) && flag != 0 ) - { - fprintf(stderr," f.%c t.%u [%d] ",funcid,t,relay); - fprintf(stderr," recv at %u from (%s) PULL these.%d lag.%d\n",(uint32_t)time(NULL),pfrom->addr.ToString().c_str(),flag,lag); - } else if ( (0) && n > 0 ) - fprintf(G->fp,"ping[%d] modval.%d from %s\n",n,m,pfrom->addr.ToString().c_str()); - } else fprintf(stderr,"ping packetsize error %d != %d, offset.%d n.%d, modval.%d purgtime.%d\n",len,offset+n*4,offset,n,m,KOMODO_DEX_PURGETIME); - if ( funcid == 'P' && haves > 0 ) - { - std::vector pong; - if ( komodo_DEXgenping('p',pong,now,m,pongbuf,haves) > 0 ) - pfrom->PushMessage("DEX",pong); - } - } // else banscore this - } - else if ( funcid == 'G' ) - { - iguana_rwnum(0,&msg[KOMODO_DEX_ROUTESIZE],sizeof(h),&h); - iguana_rwnum(0,&msg[KOMODO_DEX_ROUTESIZE+sizeof(h)],sizeof(modval),&modval); - //fprintf(G->fp," modval.%d f.%c t.%u [%d] get.%08x\n",modval,funcid,t,relay,h); - //fprintf(stderr," recv at %u from (%s)\n",(uint32_t)time(NULL),pfrom->addr.ToString().c_str()); - if ( modval >= 0 && modval < KOMODO_DEX_PURGETIME ) - { - if ( (ptr= _komodo_DEXfind(modval,h)) != 0 ) - { - //if ( GETBIT(ptr->peermask,peerpos) == 0 ) if a node specifically requests it, send it - { - return(komodo_DEXpacketsend(pfrom,peerpos,ptr,0)); // squelch relaying of 'G' packets - } - } // else fprintf(stderr,"unexpected request of %08x\n",h); - } else fprintf(stderr,"illegal modval.%d\n",modval); - } - } - //fflush(G->fp); - return(0); -} - -int32_t komodo_DEX_payloadstr(UniValue &item,uint8_t *data,int32_t datalen,int32_t decrypted) -{ - char *itemstr; int32_t i,hexflag = 0; - if ( datalen <= 0 ) - { - item.push_back(Pair((char *)"payload",(char *)"")); - item.push_back(Pair((char *)"hex",1)); - return(-1); - } - if ( data[datalen-1] != 0 ) - hexflag = 1; - else - { - for (i=0; ibytes,allocated2p,decoded,&newlen2,priv0)) != 0 ) - { - for (i=0; idata[2],sizeof(t),&t); - iguana_rwnum(0,&ptr->data[KOMODO_DEX_ROUTESIZE],sizeof(amountA),&amountA); - iguana_rwnum(0,&ptr->data[KOMODO_DEX_ROUTESIZE + sizeof(amountA)],sizeof(amountB),&amountB); - item.push_back(Pair((char *)"timestamp",(int64_t)t)); - item.push_back(Pair((char *)"id",(int64_t)komodo_DEX_id(ptr))); - bits256_str(str,ptr->hash); - item.push_back(Pair((char *)"hash",str)); - if ( komodo_DEX_tagsextract(amountA,amountB,taga,tagb,pubkeystr,destpub33,ptr) >= 0 ) - { - item.push_back(Pair((char *)"tagA",taga)); - item.push_back(Pair((char *)"tagB",tagb)); - item.push_back(Pair((char *)"pubkey",pubkeystr)); - } - memcpy(pubkey.bytes,destpub33+1,32); - komodo_DEX_payloadstr(item,&ptr->data[ptr->offset],ptr->datalen-4-ptr->offset,0); - if ( (decoded= komodo_DEX_datablobdecrypt(&senderpub,&allocated,&newlen,ptr,pubkey,taga)) != 0 ) - { - komodo_DEX_payloadstr(item,decoded,newlen,1); - if ( ptr->data[1] == 'A' && strcmp(taga,(char *)"anon") == 0 ) - { - uint8_t *anonallocated = 0,*anonallocated2=0; int32_t anonlen = newlen; char *anonmsg,senderstr[67]; bits256 senderpub; - if ( (anonmsg= (char *)komodo_DEX_anondecode(&senderpub,&anonallocated,&anonallocated2,decoded,&anonlen)) != 0 ) - { - item.push_back(Pair((char *)"anonmsg",anonmsg)); - senderstr[0] = '0'; - senderstr[1] = '1'; - bits256_str(senderstr+2,senderpub); - item.push_back(Pair((char *)"anonsender",senderstr)); - } - if ( anonallocated != 0 ) - free(anonallocated); - if ( anonallocated2 != 0 ) - free(anonallocated2); - } - } - str[0] = '0'; - str[1] = '1'; - bits256_str(str+2,senderpub); - item.push_back(Pair((char *)"senderpub",str)); - if ( newlen < 0 ) - item.push_back(Pair((char *)"error","wrong sender")); - if ( allocated != 0 ) - free(allocated), allocated = 0; - sprintf(str,"%llu.%08llu",(long long)amountA/COIN,(long long)amountA % COIN); - item.push_back(Pair((char *)"amountA",str)); - sprintf(str,"%llu.%08llu",(long long)amountB/COIN,(long long)amountB % COIN); - item.push_back(Pair((char *)"amountB",str)); - item.push_back(Pair((char *)"priority",komodo_DEX_priority(ptr->hash.ulongs[0],ptr->datalen))); - item.push_back(Pair((char *)"recvtime",(int64_t)ptr->recvtime)); - item.push_back(Pair((char *)"cancelled",(int64_t)ptr->cancelled)); - return(item); -} - -UniValue _komodo_DEXget(uint32_t shorthash) -{ - UniValue result; int32_t modval; struct DEX_datablob *ptr; - for (modval=0; modval packet; bits256 hash,pubkey; uint8_t quote[128],destpub[33],*payload=0,*payload2=0,*allocated=0; int32_t blastflag,i,m=0,ind,explen,len=0,datalen=0,destpubflag=0,slen,modval,iter; uint32_t shorthash,timestamp; uint64_t amountA=0,amountB=0; - if ( hexstr == 0 || hexstr[0] == 0 ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"broadcasting no payload is not supported")); - return(result); - } - if ( locatorp != 0 ) - *locatorp = 0; - blastflag = strcmp(hexstr,"ffff") == 0; - if ( priority < 0 || priority > KOMODO_DEX_MAXPRIORITY ) - priority = KOMODO_DEX_MAXPRIORITY; - if ( hexstr == 0 || tagA == 0 || tagB == 0 || destpub33 == 0 || volA == 0 || volB == 0 || strlen(tagA) >= KOMODO_DEX_TAGSIZE || strlen(tagB) >= KOMODO_DEX_TAGSIZE ) - return(-1); - if ( tagA[0] == 0 && tagB[0] == 0 && destpub33[0] == 0 ) - tagA = (char *)"general"; - if ( volA[0] != 0 ) - { - if ( atof(volA) < 0. ) - { - fprintf(stderr,"negative volA error\n"); - return(result); - } - amountA = komodo_DEX_convert64(volA); - //fprintf(stderr,"amountA %llu <- %s %.15f\n",(long long)amountA,volA,atof(volA) * SATOSHIDEN + 0.000000009); - } - if ( volB[0] != 0 ) - { - if ( atof(volB) < 0. ) - { - fprintf(stderr,"negative volB error\n"); - return(result); - } - amountB = komodo_DEX_convert64(volB); - } - for (iter=0; iter<10; iter++) - { - ptr = 0; - len = 0; - len = iguana_rwnum(1,"e[len],sizeof(amountA),&amountA); - len += iguana_rwnum(1,"e[len],sizeof(amountB),&amountB); - if ( is_hexstr(destpub33,0) == 66 ) - { - decode_hex(destpub,33,destpub33); - quote[len++] = 33; - destpubflag = 1; - if ( destpub[0] != 0x01 ) - { - fprintf(stderr,"only pubkey starting with 0x01 is allowed, these are available from DEX_stats\n"); - return(-1); - } - //fprintf(stderr,"set destpub\n"); - memcpy("e[len],destpub,sizeof(destpub)), len += 33; - } else quote[len++] = 0; - if ( (slen= strlen(tagA)) > 0 ) - { - //fprintf(stderr,"tagA (%s)\n",tagA); - quote[len++] = slen; - memcpy("e[len],tagA,slen), len += slen; - } else quote[len++] = 0; - if ( (slen= strlen(tagB)) > 0 ) - { - //fprintf(stderr,"tagB (%s)\n",tagB); - quote[len++] = slen; - memcpy("e[len],tagB,slen), len += slen; - } else quote[len++] = 0; - if ( blastflag != 0 ) - { - for (i=len; i<(int32_t)(sizeof(quote)/sizeof(*quote)); i++) - quote[i] = (rand() >> 11) & 0xff; - len = i; - } - else if ( (datalen= is_hexstr(hexstr,0)) == (int32_t)strlen(hexstr) && datalen > 1 && (datalen&1) == 0 ) - { - datalen >>= 1; - //fprintf(stderr,"offset.%d datalen.%d (%s)\n",len,datalen,hexstr); - payload = (uint8_t *)malloc(datalen); - decode_hex(payload,datalen,hexstr); - } - else if ( (datalen= (int32_t)strlen(hexstr)) > 0 ) - { - payload = (uint8_t *)hexstr; - datalen++; - } - allocated = 0; - timestamp = (uint32_t)time(NULL); - modval = (timestamp % KOMODO_DEX_PURGETIME); - if ( destpubflag != 0 ) - { - bits256 priv0; - komodo_DEX_privkey(priv0); - if ( strcmp(tagA,"inbox") == 0 ) - { - for (i=0; i<32; i++) - pubkey.bytes[i] = destpub[i + 1]; - } else pubkey = GENESIS_PUBKEY; - if ( (payload2= komodo_DEX_encrypt(&allocated,payload,&datalen,pubkey,priv0)) == 0 ) - { - fprintf(stderr,"encryption error for datalen.%d\n",datalen); - if ( allocated != 0 ) - free(allocated), allocated = 0; - memset(priv0.bytes,0,sizeof(priv0)); - return(0); - } - else if ( (0) ) - { - for (i=0; i priority + KOMODO_DEX_BLAST ) - { - //fprintf(stderr,"skip harder than specified %d vs %d\n",komodo_DEX_priority(hash.ulongs[0],explen), priority + iter + komodo_DEX_sizepriority(explen)); - continue; - } - if ( m > KOMODO_DEX_MAXPACKETSIZE ) - { - fprintf(stderr,"packetsize.%d > KOMODO_DEX_MAXPACKETSIZE.%d\n",m,KOMODO_DEX_MAXPACKETSIZE); - return(0); - } - { - pthread_mutex_lock(&DEX_globalmutex); - iguana_rwnum(0,&packet[2],sizeof(timestamp),×tamp); - modval = (timestamp % KOMODO_DEX_PURGETIME); - if ( (ptr= _komodo_DEXfind(modval,shorthash)) == 0 ) - { - if ( (ptr= _komodo_DEXadd(timestamp,modval,hash,shorthash,&packet[0],packet.size())) == 0 ) - { - char str[65]; - for (i=0; idata[2],sizeof(timestamp),×tamp); - *locatorp = ((uint64_t)timestamp << 32) | ptr->shorthash; - } - return(result); - } else return(0); -} - -int32_t _komodo_DEX_gettips(struct DEX_index *tips[KOMODO_DEX_MAXINDICES],int8_t &lenA,char *tagA,int8_t &lenB,char *tagB,int8_t &plen,uint8_t *destpub,char *destpub33,uint64_t &minamountA,char *minA,uint64_t &maxamountA,char *maxA,uint64_t &minamountB,char *minB,uint64_t &maxamountB,char *maxB) -{ - memset(tips,0,sizeof(*tips)*KOMODO_DEX_MAXINDICES); - if ( tagA != 0 ) - lenA = (int32_t)strlen(tagA); - if ( tagB != 0 ) - lenB = (int32_t)strlen(tagB); - if ( tagA == 0 || tagB == 0 || destpub33 == 0 || minA == 0 || maxA == 0 || minB == 0 || maxB == 0 || lenA >= KOMODO_DEX_TAGSIZE || lenB >= KOMODO_DEX_TAGSIZE ) - return(-1); - if ( tagA[0] == 0 && tagB[0] == 0 && destpub33[0] == 0 ) - tagA = (char *)"general"; - if ( minA[0] != 0 ) - { - if ( atof(minA) < 0. ) - { - fprintf(stderr,"negative minA error\n"); - return(-2); - } - minamountA = komodo_DEX_convert64(minA); - } - if ( maxA[0] != 0 ) - { - if ( atof(maxA) < 0. ) - { - fprintf(stderr,"negative maxA error\n"); - return(-3); - } - maxamountA = komodo_DEX_convert64(maxA); - } - if ( minB[0] != 0 ) - { - if ( atof(minB) < 0. ) - { - fprintf(stderr,"negative minB error\n"); - return(-4); - } - minamountB = komodo_DEX_convert64(minB); - } - if ( maxB[0] != 0 ) - { - if ( atof(maxB) < 0. ) - { - fprintf(stderr,"negative maxB error\n"); - return(-5); - } - maxamountB = komodo_DEX_convert64(maxB); - } - if ( minA > maxA || minB > maxB ) - { - fprintf(stderr,"illegal value range [%.8f %.8f].A [%.8f %.8f].B\n",dstr(minamountA),dstr(maxamountA),dstr(minamountB),dstr(maxamountB)); - return(-6); - } - if ( is_hexstr(destpub33,0) == 66 ) - { - decode_hex(destpub,33,destpub33); - plen = 33; - } - //fprintf(stderr,"DEX_list (%s) (%s)\n",tagA,tagB); - return(_DEX_updatetips(tips,0,0,lenA,(uint8_t *)tagA,lenB,(uint8_t *)tagB,destpub,plen) & 0xffff); -} - -int32_t komodo_DEX_ptrfilter(uint64_t &amountA,uint64_t &amountB,struct DEX_datablob *ptr,int32_t minpriority,int8_t lenA,char *tagA,int8_t lenB,char *tagB,int8_t plen,uint8_t *destpub,uint64_t minamountA,uint64_t maxamountA,uint64_t minamountB,uint64_t maxamountB) -{ - int32_t priority,skipflag = 0; - amountA = amountB = 0; - if ( komodo_DEX_tagsmatch(amountA,amountB,ptr,(uint8_t *)tagA,lenA,(uint8_t *)tagB,lenB,destpub,plen) < 0 ) - { - //fprintf(stderr,"skip %p due to no tagsmatch\n",ptr); - skipflag = 1; - } - else if ( (priority= komodo_DEX_priority(ptr->hash.ulongs[0],ptr->datalen)) < minpriority ) - { - //fprintf(stderr,"priority.%d < min.%d, skip\n",komodo_DEX_priority(ptr->hash.ulongs[0],ptr->datalen),minpriority); - skipflag = 2; - } - else - { - iguana_rwnum(0,&ptr->data[KOMODO_DEX_ROUTESIZE],sizeof(amountA),&amountA); - iguana_rwnum(0,&ptr->data[KOMODO_DEX_ROUTESIZE + sizeof(amountA)],sizeof(amountB),&amountB); - if ( amountA < minamountA || amountA > maxamountA ) - { - fprintf(stderr,"amountA %.8f vs min %.8f max %.8f, skip\n",dstr(amountA),dstr(minamountA),dstr(maxamountA)); - skipflag = 3; - } - else if ( amountB < minamountB || amountB > maxamountB ) - { - fprintf(stderr,"amountB %.8f vs min %.8f max %.8f, skip\n",dstr(amountB),dstr(minamountB),dstr(maxamountB)); - skipflag = 4; - } - } - return(skipflag); -} - -UniValue _komodo_DEXlist(uint32_t stopat,int32_t minpriority,char *tagA,char *tagB,char *destpub33,char *minA,char *maxA,char *minB,char *maxB,char *stophashstr) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); struct DEX_datablob *ptr; int32_t err,ind,n=0,skipflag; bits256 stophash; struct DEX_index *tips[KOMODO_DEX_MAXINDICES],*index; uint64_t minamountA=0,maxamountA=(1LL<<63),minamountB=0,maxamountB=(1LL<<63),amountA,amountB; int8_t lenA=0,lenB=0,plen=0; uint8_t destpub[33]; uint32_t thislist; - if ( stophashstr != 0 && is_hexstr(stophashstr,0) == 64 ) - decode_hex(stophash.bytes,32,stophashstr); - else memset(stophash.bytes,0,32); - //fprintf(stderr,"DEX_list (%s) (%s)\n",tagA,tagB); - if ( (err= _komodo_DEX_gettips(tips,lenA,tagA,lenB,tagB,plen,destpub,destpub33,minamountA,minA,maxamountA,maxA,minamountB,minB,maxamountB,maxB)) < 0 ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"errcode",err)); - return(result); - } - thislist = komodo_DEX_listid(); - n = 0; - for (ind=0; indtail; ptr!=0; ptr=ptr->prevs[ind]) - { - if ( (stopat != 0 && komodo_DEX_id(ptr) == stopat) || memcmp(stophash.bytes,ptr->hash.bytes,32) == 0 ) - break; - skipflag = komodo_DEX_ptrfilter(amountA,amountB,ptr,minpriority,lenA,tagA,lenB,tagB,plen,destpub,minamountA,maxamountA,minamountB,maxamountB); - if ( skipflag == 0 && ptr->lastlist != thislist ) - { - ptr->lastlist = thislist; - //fprintf(stderr,"%u ",ptr->shorthash); - a.push_back(komodo_DEX_dataobj(ptr)); - n++; - } - if ( ptr == index->head ) - break; - } - } - } - //fprintf(stderr,"ids\n"); - result.push_back(Pair((char *)"result",(char *)"success")); - result.push_back(Pair((char *)"matches",a)); - result.push_back(Pair((char *)"tagA",tagA)); - result.push_back(Pair((char *)"tagB",tagB)); - result.push_back(Pair((char *)"pubkey",destpub33)); - result.push_back(Pair((char *)"n",n)); - return(result); -} - -// orderbook support - -static int _cmp_orderbook(const void *a,const void *b) // bids -{ - int32_t retval = 0; -#define ptr_a (*(struct DEX_orderbookentry **)a)->price -#define ptr_b (*(struct DEX_orderbookentry **)b)->price - if ( ptr_b > ptr_a+SMALLVAL ) - retval = -1; - else if ( ptr_b < ptr_a-SMALLVAL ) - retval = 1; - else - { -#undef ptr_a -#undef ptr_b -#define ptr_a (*(struct DEX_orderbookentry **)a)->amountA -#define ptr_b (*(struct DEX_orderbookentry **)b)->amountA - //printf("cmp %.8f vs %.8f <- %p %p\n",dstr(ptr_a),dstr(ptr_b),a,b); - if ( ptr_b > ptr_a ) - return(1); - else if ( ptr_b < ptr_a ) - return(-1); - } - // printf("%.8f vs %.8f -> %d\n",ptr_a,ptr_b,retval); - return(retval); -#undef ptr_a -#undef ptr_b -} - -static int _revcmp_orderbook(const void *a,const void *b) // asks -{ - int32_t retval = 0; -#define ptr_a (*(struct DEX_orderbookentry **)a)->price -#define ptr_b (*(struct DEX_orderbookentry **)b)->price - if ( ptr_b > ptr_a+SMALLVAL ) - retval = 1; - else if ( ptr_b < ptr_a-SMALLVAL ) - retval = -1; - else - { -#undef ptr_a -#undef ptr_b -#define ptr_a (*(struct DEX_orderbookentry **)a)->amountA -#define ptr_b (*(struct DEX_orderbookentry **)b)->amountA - //printf("revcmp %.8f vs %.8f <- %p %p\n",dstr(ptr_a),dstr(ptr_b),a,b); - if ( ptr_b > ptr_a ) - return(1); - else if ( ptr_b < ptr_a ) - return(-1); - } - // printf("%.8f vs %.8f -> %d\n",ptr_a,ptr_b,retval); - return(retval); -#undef ptr_a -#undef ptr_b -} - -UniValue DEX_orderbookjson(struct DEX_orderbookentry *op) -{ - UniValue item(UniValue::VOBJ); char str[67]; int32_t i; - if ( op->price >= 0.00000001-SMALLVAL ) - sprintf(str,"%0.8f",op->price + 0.0000000049); - else sprintf(str,"%0.15f",op->price); - item.push_back(Pair((char *)"price",str)); - sprintf(str,"%0.8f",((double)op->amountA + 0.0000000049)/SATOSHIDEN); - item.push_back(Pair((char *)"baseamount",str)); - sprintf(str,"%0.8f",((double)op->amountB + 0.0000000049)/SATOSHIDEN); - item.push_back(Pair((char *)"relamount",str)); - item.push_back(Pair((char *)"priority",(int64_t)op->priority)); - for (i=0; i<33; i++) - sprintf(&str[i<<1],"%02x",op->pubkey33[i]); - str[i<<1] = 0; - item.push_back(Pair((char *)"pubkey",str)); - item.push_back(Pair((char *)"timestamp",(int64_t)op->timestamp)); - bits256_str(str,op->hash); - item.push_back(Pair((char *)"hash",str)); - item.push_back(Pair((char *)"id",(int64_t)op->shorthash)); - return(item); -} - -struct DEX_orderbookentry *DEX_orderbookentry(struct DEX_datablob *ptr,int32_t revflag,char *base,char *rel) -{ - struct DEX_orderbookentry *op; uint64_t amountA,amountB; double price = 0.; - char taga[KOMODO_DEX_MAXKEYSIZE+1],tagb[KOMODO_DEX_MAXKEYSIZE+1],pubkeystr[67]; uint8_t destpub33[33]; - if ( komodo_DEX_tagsextract(amountA,amountB,taga,tagb,pubkeystr,destpub33,ptr) == 0 ) - { - if ( strcmp(taga,base) != 0 || strcmp(tagb,rel) != 0 ) - return(0); - } - if ( (op= (struct DEX_orderbookentry *)calloc(1,sizeof(*op))) != 0 ) - { - memcpy(op->pubkey33,destpub33,33); - iguana_rwnum(0,&ptr->data[KOMODO_DEX_ROUTESIZE],sizeof(amountA),&amountA); - iguana_rwnum(0,&ptr->data[KOMODO_DEX_ROUTESIZE + sizeof(amountA)],sizeof(amountB),&amountB); - if ( revflag == 0 ) - { - op->amountA = amountA; - op->amountB = amountB; - if ( amountA != 0 ) - price = (double)amountB / amountA; - } - else - { - op->amountA = amountB; - op->amountB = amountA; - if ( amountB != 0 ) - price = (double)amountA / amountB; - } - op->price = price; - iguana_rwnum(0,&ptr->data[2],sizeof(op->timestamp),&op->timestamp); - op->hash = ptr->hash; - op->shorthash = _komodo_DEXquotehash(ptr->hash,ptr->datalen); - op->priority = ptr->priority; - - } - return(op); -} - -UniValue _komodo_DEXorderbook(int32_t revflag,int32_t maxentries,int32_t minpriority,char *tagA,char *tagB,char *destpub33,char *minA,char *maxA,char *minB,char *maxB) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); struct DEX_orderbookentry *op; std::vectororders; struct DEX_datablob *ptr; uint32_t thislist; int32_t i,err,ind,n=0,skipflag; struct DEX_index *tips[KOMODO_DEX_MAXINDICES],*index; uint64_t minamountA=0,maxamountA=(1LL<<63),minamountB=0,maxamountB=(1LL<<63),amountA,amountB; int8_t lenA=0,lenB=0,plen=0; uint8_t destpub[33]; - if ( maxentries <= 0 ) - maxentries = 10; - if ( tagA[0] == 0 || tagB[0] == 0 ) - { - fprintf(stderr,"need both tagA and tagB to specify base/rel for orderbook\n"); - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"errcode",-13)); - return(result); - } - if ( (err= _komodo_DEX_gettips(tips,lenA,tagA,lenB,tagB,plen,destpub,destpub33,minamountA,minA,maxamountA,maxA,minamountB,minB,maxamountB,maxB)) < 0 ) - { - //fprintf(stderr,"couldnt find any\n"); - return(a); - } - thislist = komodo_DEX_listid(); - n = 0; - for (ind=KOMODO_DEX_MAXINDICES-1; indtail; ptr!=0; ptr=ptr->prevs[ind]) - { - skipflag = komodo_DEX_ptrfilter(amountA,amountB,ptr,minpriority,lenA,tagA,lenB,tagB,plen,destpub,minamountA,maxamountA,minamountB,maxamountB); - if ( skipflag == 0 && ptr->cancelled == 0 && amountA != 0 && amountB != 0 ) - { - if ( ptr->lastlist != thislist && (op= DEX_orderbookentry(ptr,revflag,tagA,tagB)) != 0 ) // - { - //fprintf(stderr,"ADD n.%d lastlist.%u vs %u\n",n,ptr->lastlist,thislist); - ptr->lastlist = thislist; - orders.push_back(op); - n++; - } else fprintf(stderr,"skip ptr->lastlist.%u vs thislist.%d\n",ptr->lastlist,thislist); - } //else fprintf(stderr,"skipflag.%d cancelled.%u plen.%d amountA %.8f amountB %.8f\n",skipflag,ptr->cancelled,plen,dstr(amountA),dstr(amountB)); - if ( ptr == index->head ) - break; - } - } - } - if ( n > 0 ) - { - //fprintf(stderr,"sort %d orders for %s/%s\n",n,tagA,tagB); - qsort(&orders[0],n,sizeof(struct DEX_orderbookentry *),revflag != 0 ? _revcmp_orderbook : _cmp_orderbook); - for (i=0; i= 0 ) - result.push_back(Pair((char *)"progress",(double)DEX_progress/100.)); - memset(histo,0,sizeof(histo)); - totalhash = _komodo_DEXtotal(histo,total); - sprintf(logstr,"RAM.%d %08x R.%lld S.%lld A.%lld dup.%lld | L.%lld A.%lld coll.%lld | lag (%.4f %.4f %.4f) err.%lld pend.%lld T/F %lld/%lld | ",total,totalhash,(long long)DEX_totalrecv,(long long)DEX_totalsent,(long long)DEX_totaladd,(long long)DEX_duplicate,(long long)DEX_lookup32,(long long)DEX_add32,(long long)DEX_collision32,DEX_lag,DEX_lag2,DEX_lag3,(long long)DEX_maxlag,(long long)DEX_Numpending,(long long)DEX_truncated,(long long)DEX_freed); - for (i=13; i>=0; i--) - sprintf(logstr+strlen(logstr),"%.0f ",(double)histo[i]);//1000.*histo[i]/(total+1)); // expected 1 1 2 5 | 10 10 10 10 10 | 10 9 9 7 5 - if ( (d= (now-lasttime)) <= 0 ) - d = 1; - sprintf(logstr+strlen(logstr),"%s %lld/sec",komodo_DEX_islagging()!=0?"LAG":"",(long long)(DEX_totaladd - lastadd)/d); - lasttime = now; - lastadd = DEX_totaladd; - result.push_back(Pair((char *)"perfstats",logstr)); - pthread_mutex_unlock(&DEX_globalmutex); - return(result); -} - -UniValue komodo_DEXcancel(char *pubkeystr,uint32_t shorthash,char *tagA,char *tagB) -{ - UniValue result(UniValue::VOBJ); uint8_t hex[34],pub33[33]; char hexstr[67],checkstr[67]; int32_t i,lenA,lenB,len=0; - checkstr[0] = '0'; - checkstr[1] = '1'; - bits256_str(checkstr+2,DEX_pubkey); - decode_hex(pub33,33,checkstr); - if ( shorthash != 0 ) - { - len = iguana_rwnum(1,&hex[len],sizeof(shorthash),&shorthash); - { - pthread_mutex_lock(&DEX_globalmutex); - _komodo_DEX_cancelid(shorthash,DEX_pubkey,(uint32_t)time(NULL)); - pthread_mutex_unlock(&DEX_globalmutex); - } - } - else if ( pubkeystr[0] != 0 ) - { - if ( strcmp(checkstr,pubkeystr) != 0 ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"wrong pubkey")); - result.push_back(Pair((char *)"pubkey",pubkeystr)); - result.push_back(Pair((char *)"correct pubkey",checkstr)); - return(result); - } - decode_hex(hex,33,checkstr); - len = 33; - { - pthread_mutex_lock(&DEX_globalmutex); - _komodo_DEX_cancelpubkey((char *)"",(char *)"",pub33,(uint32_t)time(NULL)); - pthread_mutex_unlock(&DEX_globalmutex); - } - } - else if ( tagA[0] != 0 && tagB[0] != 0 ) - { - lenA = (int32_t)strlen(tagA); - lenB = (int32_t)strlen(tagB); - if ( lenA >= KOMODO_DEX_TAGSIZE || lenB >= KOMODO_DEX_TAGSIZE ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"too long tags")); - result.push_back(Pair((char *)"tagA",tagA)); - result.push_back(Pair((char *)"tagB",tagB)); - return(result); - } - hex[len++] = lenA; - memcpy(&hex[len],tagA,lenA), len += lenA; - hex[len++] = lenB; - memcpy(&hex[len],tagB,lenB), len += lenB; - { - pthread_mutex_lock(&DEX_globalmutex); - _komodo_DEX_cancelpubkey(tagA,tagB,pub33,(uint32_t)time(NULL)); - pthread_mutex_unlock(&DEX_globalmutex); - } - } - for (i=0; i 0 ) - { - if ( (index= tips[ind]) != 0 ) // pubkey list should be shortest, on average - { - for (ptr=index->tail; ptr!=0; ptr=ptr->prevs[ind]) - { - if ( ptr->cancelled == 0 && komodo_DEX_tagsmatch(amountA,amountB,ptr,(uint8_t *)tagA,lenA,(uint8_t *)tagB,lenB,pubkey33,plen) == 0 ) - { - if ( strcmp(tagA,(char *)"slices") != 0 || amountA/COIN == offset0 ) - { - iguana_rwnum(0,&ptr->data[2],sizeof(t),&t); - if ( t > latest ) - { - latest = t; - latestptr = ptr; - } - } - //if ( strcmp(tagA,(char *)"slices") == 0 ) - // fprintf(stderr,"slices amountA %llu vs %llu\n",(long long)amountA/COIN,(long long)offset0); - } - if ( ptr == index->head ) - break; - } - } else fprintf(stderr,"gettips error.%d\n",errflag); - } - return(latestptr); -} - -int32_t komodo_DEX_locatorsload(uint64_t *locators,uint64_t *offset0p,int32_t *numlocatorsp,char *locatorfname) -{ - FILE *fp; int32_t i,j,errflag=0,len,lag; uint64_t locator; uint32_t now = (uint32_t)time(NULL); - *numlocatorsp = 0; - if ( (fp= fopen(locatorfname,(char *)"rb")) != 0 ) - { - errflag = 0; - fseek(fp,0,SEEK_END); - len = (int32_t)ftell(fp); - rewind(fp); - if ( fread(offset0p,1,sizeof(*offset0p),fp) != sizeof(*offset0p) ) - errflag++; - for (i=sizeof(*offset0p),j=0; i> 32)); - if ( lag > KOMODO_DEX_PURGETIME-KOMODO_DEX_MAXLAG ) - { - //fprintf(stderr,"purged locator[%d] %u %08x, lag %d\n",j,(uint32_t)(locator >> 32),(uint32_t)locator,lag); - locators[j] = 0; - } - } - *numlocatorsp = j; - fclose(fp), fp = 0; - } - if ( errflag != 0 ) - fprintf(stderr,"locators load numerrs.%d\n",errflag); - return(-errflag); -} - -int32_t komodo_DEX_request(int32_t priority,uint32_t shorthash,uint32_t timestamp,char *tagA,char *tagB) -{ - uint8_t hex[16]; char str[67],hexstr[33]; int32_t i,n=0; - iguana_rwnum(1,&hex[0],sizeof(shorthash),&shorthash); - iguana_rwnum(1,&hex[sizeof(shorthash)],sizeof(timestamp),×tamp); - for (i=0; i> 32; - h = locator & 0xffffffff; - { - pthread_mutex_lock(&DEX_globalmutex); - fragptr = _komodo_DEXfind(t % KOMODO_DEX_PURGETIME,h); - pthread_mutex_unlock(&DEX_globalmutex); - } - errflag = 0; - if ( fragptr != 0 ) - { - if ( (fraglen= komodo_DEX_decryptbuf(buf,sizeof(buf),fragptr,senderpub,(char *)tagA)) > 0 ) - { - fseek(fp,offset,SEEK_SET); - if ( fwrite(buf,1,fraglen,fp) != fraglen ) - { - fprintf(stderr,"write error offset %ld %ld\n",offset,ftell(fp)); - errflag = 1; - } - else - { - written++; - //fprintf(stderr,"write %s:%ld [%d] sizepriority.%d\n",fname,i*sizeof(buf)+offset0,fraglen,komodo_DEX_sizepriority(fragptr->datalen)); - } - } - else - { - fprintf(stderr,"error decrypting into buf for offset of %ld, fraglen.%d datalen.%d h.%u\n",offset,fraglen,fragptr->datalen,h); - errflag = 1; - } - } - else - { - errflag = 1; - //fprintf(stderr,"%s: missing t.%u h.%08x\n",fname,t % KOMODO_DEX_PURGETIME,h); - needrequest = 1; - } - return(-errflag); -} - -UniValue komodo_DEXsubscribe(int32_t &cmpflag,char *origfname,int32_t priority,uint32_t shorthash,char *publisher,int32_t sliceid) -{ - static uint64_t locators[KOMODO_DEX_MAXPACKETSIZE/sizeof(uint64_t)+1],zero[4]; - static uint64_t prevlocators[KOMODO_DEX_MAXPACKETSIZE/sizeof(uint64_t)+1]; - UniValue result(UniValue::VOBJ); FILE *fp; int32_t i,j,n,num,written=0,numprev,fraglen,errflag,modval,requestflag=0,missing=0,len=0,newlen=0; bits256 senderpub,pubkey,filehash; uint8_t tagA[KOMODO_DEX_TAGSIZE+1],tagB[KOMODO_DEX_TAGSIZE+1],pubkey33[33],*decoded,*allocated=0,hex[8]; struct DEX_datablob *fragptr,*ptr = 0; char str[67],pubkeystr[67],fname[512],tagBstr[33],fullfname[512],locatorfname[512]; bits256 checkhash; uint32_t t,h; uint64_t locator,amountA,amountB,mult,prevoffset0,offset0=0; int8_t lenA,lenB,plen; - cmpflag = 0; - if ( sliceid < 0 ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"negative sliceid")); - result.push_back(Pair((char *)"sliceid",(int64_t)sliceid)); - return(result); - } - if ( publisher == 0 || publisher[0] == 0 || strlen(publisher) != 66 ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"need publisher pubkey for sub")); - return(result); - } - mult = KOMODO_DEX_FILEBUFSIZE * KOMODO_DEX_STREAMSIZE; - if ( sliceid > 0 ) - { - offset0 = ((uint64_t)sliceid - 1) * mult; - sprintf(fname,"%s.%llu",origfname,(long long)offset0); - sprintf(tagBstr,"%llu",(long long)offset0); - } - else - { - strcpy(fname,origfname); - sprintf(tagBstr,"locators"); - } - { - pthread_mutex_lock(&DEX_globalmutex); - memset(checkhash.bytes,0,sizeof(checkhash)); - if ( (ptr= _komodo_DEX_latestptr(sliceid == 0 ? (char *)"files" : (char *)"slices",origfname,publisher,offset0)) != 0 ) - { - if ( (decoded= komodo_DEX_datablobdecrypt(&senderpub,&allocated,&newlen,ptr,*(bits256 *)&zero,sliceid == 0 ? (char *)"files" : (char *)"slices")) != 0 && newlen == 32 ) - { - memcpy(checkhash.bytes,decoded,32); - //for (i=0; i<32; i++) - // fprintf(stderr,"%02x",decoded[i]); - } - if ( allocated != 0 ) - free(allocated), allocated = 0; - //fprintf(stderr," datalen.%d for %s sliceid.%d newlen.%d\n",ptr->datalen,fname,sliceid,newlen); - } - if ( shorthash == 0 ) - { - //if ( sliceid == 0 ) - // sprintf(tagBstr,"locators"); - //else sprintf(tagBstr,"%llu",(long long)offset0); - if ( (ptr= _komodo_DEX_latestptr(origfname,tagBstr,publisher,0)) != 0 ) - shorthash = ptr->shorthash; - //fprintf(stderr,"fname.%s auto search %s %s %s shorthash.%08x sliceid.%d\n",fname,origfname,tagBstr,publisher,shorthash,sliceid); - } - if ( shorthash != 0 ) - { - for (modval=0; modvaldata[KOMODO_DEX_ROUTESIZE],ptr->datalen-KOMODO_DEX_ROUTESIZE) < 0 ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"couldnt extract tags")); - return(result); - } - if ( strcmp((char *)tagA,origfname) != 0 || strcmp((char *)tagB,tagBstr) != 0 ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"tags mismatch")); - result.push_back(Pair((char *)"tagA",(char *)tagA)); - result.push_back(Pair((char *)"filename",origfname)); - result.push_back(Pair((char *)"tagB",(char *)tagB)); - result.push_back(Pair((char *)"tagBstr",(char *)tagBstr)); - result.push_back(Pair((char *)"sliceid",(int64_t)sliceid)); - return(result); - } - memcpy(pubkey.bytes,pubkey33+1,32); - if ( (decoded= komodo_DEX_datablobdecrypt(&senderpub,&allocated,&newlen,ptr,pubkey,(char *)tagA)) != 0 && (newlen & 7) == 0 ) - { - iguana_rwnum(0,&decoded[0],sizeof(offset0),&offset0); - for (i=sizeof(offset0),j=0; i 0 ) - fprintf(stderr,"number of fragments written: %d\n",written); - if ( requestflag != 0 ) - { - iguana_rwnum(0,&ptr->data[2],sizeof(t),&t); - n = komodo_DEX_request(priority,shorthash,t,(char *)origfname,(char *)"request"); - result.push_back(Pair((char *)"status","request sent to get missing blocks")); - result.push_back(Pair((char *)"n",n)); - } - return(result); -} - -UniValue komodo_DEXpublish(char *fname,int32_t priority,int32_t sliceid) -{ - static uint8_t locators[KOMODO_DEX_MAXPACKETSIZE]; - UniValue result(UniValue::VOBJ); FILE *fp,*oldfp=0; uint64_t locator,filesize=0,volA,offset0=0,prevoffset0; long fsize; int32_t i,rlen,rescan=0,n,cmpflag,numprev,oldn=0,numlocators=0,changed=0,mult; bits256 filehash; uint8_t buf[KOMODO_DEX_FILEBUFSIZE],oldbuf[KOMODO_DEX_FILEBUFSIZE],zeros[sizeof(uint64_t)]; char bufstr[sizeof(buf)*2+1],pubkeystr[67],str[65],fname2[512],volAstr[16],volBstr[16],locatorfname[512],oldfname[512],*hexstr; - DEX_progress = 0; - if ( sliceid < 0 ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"negative sliceid")); - result.push_back(Pair((char *)"sliceid",(int64_t)sliceid)); - return(result); - } - mult = KOMODO_DEX_FILEBUFSIZE * KOMODO_DEX_STREAMSIZE; - pubkeystr[0] = '0'; - pubkeystr[1] = '1'; - bits256_str(&pubkeystr[2],DEX_pubkey); - if ( sliceid > 0 ) - { - offset0 = ((uint64_t)sliceid - 1) * mult; - sprintf(oldfname,"%s.%llu.%s",fname,(long long)offset0,pubkeystr); - } else sprintf(oldfname,"%s.%s",fname,pubkeystr); - sprintf(locatorfname,"%s.locators",oldfname); - if ( (fp= fopen(oldfname,"rb")) == 0 ) - rescan = 1; - else fclose(fp), fp = 0; - if ( (fp= fopen(locatorfname,"rb")) == 0 ) - rescan = 1; - else fclose(fp), fp = 0; - //fprintf(stderr,"fnames (%s): (%s) and (%s) rescan.%d\n",fname,oldfname,locatorfname,rescan); - if ( strlen(fname) >= KOMODO_DEX_TAGSIZE ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"filename longer than 15 chars")); - result.push_back(Pair((char *)"filename",fname)); - return(result); - } - else if ( (fp= fopen(fname,(char *)"rb")) == 0 ) - { - char altname[512],*appdata; -#ifdef _WIN32 - if ( (appdata= getenv("APPDATA")) != 0 ) - sprintf(altname,"%s\\dexp2p\\%s",appdata,fname); - else sprintf(altname,"C:\\tmp\\dexp2p\\%s",fname); -#else - if ( (appdata= getenv("HOME")) != 0 ) - sprintf(altname,"%s/dexp2p/%s",appdata,fname); - else sprintf(altname,"/usr/local/dexp2p/%s",fname); -#endif - if ( (fp= fopen(altname,(char *)"rb")) == 0 ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"file not found")); - result.push_back(Pair((char *)"filename",fname)); - result.push_back(Pair((char *)"altname",altname)); - return(result); - } - } - fseek(fp,0,SEEK_END); - fsize = ftell(fp); - if ( sliceid == 0 ) - { - if ( fsize/sizeof(buf) > (sizeof(locators)-sizeof(uint64_t))/sizeof(uint64_t) ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"file too big")); - result.push_back(Pair((char *)"filename",fname)); - result.push_back(Pair((char *)"filesize",(int64_t)fsize)); - fclose(fp), fp = 0; - return(result); - } - komodo_DEXsubscribe(cmpflag,fname,priority,0,pubkeystr,0); - } - else - { - fsize -= offset0; - if ( fsize > mult ) - fsize = mult; - rescan = 1; - //komodo_DEXsubscribe(cmpflag,fname,priority,0,pubkeystr,sliceid); - } - memset(locators,0,sizeof(locators)); - if ( rescan == 0 && komodo_DEX_locatorsload((uint64_t *)&locators[sizeof(offset0)],&prevoffset0,&numprev,locatorfname) == 0 ) - { - if ( (oldfp= fopen(oldfname,"rb")) != 0 && rescan == 0 ) - { - fseek(oldfp,0,SEEK_END); - oldn = (int32_t)(ftell(oldfp) / sizeof(buf)); - } - } else rescan = 1; - n = (int32_t)(fsize / sizeof(buf)); - if ( n < 0 ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"sliceid beyond end of file")); - result.push_back(Pair((char *)"sliceid",(int64_t)sliceid)); - result.push_back(Pair((char *)"filesize",(int64_t)fsize)); - result.push_back(Pair((char *)"streamstart",(int64_t)sliceid*mult)); - fclose(fp), fp = 0; - return(result); - } - //fprintf(stderr,"rescan.%d offset0.%llu vs prev %llu numprev.%d oldn.%d\n",rescan,(long long)offset0,(long long)prevoffset0,numprev,oldn); - if ( sliceid != 0 && n > KOMODO_DEX_STREAMSIZE ) - n = KOMODO_DEX_STREAMSIZE; - iguana_rwnum(1,&locators[0],sizeof(offset0),&offset0); - for (volA=0; volA<=n; volA++) - { - if ( sliceid != 0 && volA >= KOMODO_DEX_STREAMSIZE ) - break; - fseek(fp,volA * sizeof(buf) + offset0,SEEK_SET); - if ( volA == n ) - rlen = (fsize - volA*sizeof(buf)); - else rlen = sizeof(buf); - if ( rescan == 0 && volA < numprev ) - { - iguana_rwnum(0,&locators[volA*sizeof(uint64_t) + sizeof(uint64_t)],sizeof(locator),&locator); - //fprintf(stderr,"%d of %d: %llx\n",(int32_t)volA,numprev,(long long)locator); - if ( locator != 0 ) - { - numlocators++; - if ( rlen > 0 ) - filesize += rlen; - continue; - } - } - //fprintf(stderr,"%d of %d: rlen.%d\n",(int32_t)volA,n,rlen); - if ( rlen > 0 ) - { - filesize += rlen; - if ( oldfp != 0 ) - fseek(oldfp,ftell(fp) - offset0,SEEK_SET); - if ( fread(buf,1,rlen,fp) == rlen ) - { - iguana_rwnum(0,&locators[volA*sizeof(uint64_t) + sizeof(uint64_t)],sizeof(locator),&locator); - if ( locator == 0 || oldfp == 0 || fread(oldbuf,1,rlen,oldfp) != rlen || memcmp(buf,oldbuf,rlen) != 0 ) - { - for (i=0; i> 32) % KOMODO_DEX_PURGETIME,(uint32_t)locator,(long long)*(uint64_t *)&locators[volA*sizeof(uint64_t) + sizeof(uint64_t)],rlen); - } - else - { - locator = *(uint64_t *)&locators[volA*sizeof(uint64_t) + sizeof(uint64_t)]; - //fprintf(stderr,"recycle locator.%d of %d: m.%d %08x %llx\n",(int32_t)volA,n,(uint32_t)(locator >> 32) % KOMODO_DEX_PURGETIME,(uint32_t)locator,(long long)locator); - } - numlocators++; - } - else - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"file read error")); - result.push_back(Pair((char *)"filename",fname)); - fclose(fp), fp = 0; - if ( oldfp != 0 ) - fclose(oldfp), oldfp = 0;; - return(result); - } - } - } - if ( sliceid == 0 ) - filehash = komodo_DEX_filehash(fp,0,fsize,fname); - else filehash = komodo_DEX_filehash(fp,offset0,filesize,fname); - DEX_progress = -1; - if ( changed != 0 ) - { - hexstr = (char *)calloc(1,65+(numlocators+1)*sizeof(uint64_t)*2+1); - init_hexbytes_noT(hexstr,locators,(int32_t)((numlocators+1) * sizeof(uint64_t))); - sprintf(volAstr,"%llu.%08llu",(long long)filesize/COIN,(long long)filesize % COIN); - sprintf(volBstr,"%llu.%08llu",(long long)numlocators/COIN,(long long)numlocators % COIN); - if ( sliceid == 0 ) - { - komodo_DEXbroadcast(0,'Q',hexstr,priority+KOMODO_DEX_VIPLEVEL,fname,(char *)"locators",pubkeystr,volAstr,volBstr); - bits256_str(hexstr,filehash); - komodo_DEXbroadcast(0,'Q',hexstr,priority+KOMODO_DEX_VIPLEVEL,(char *)"files",fname,pubkeystr,volAstr,volBstr); - //fprintf(stderr,"broadcast fname.(%s) (%s) filehash.(%s)\n",fname,hexstr,bits256_str(str,filehash)); - } - else - { - sprintf(str,"%llu",(long long)offset0); - komodo_DEXbroadcast(0,'Q',hexstr,priority+KOMODO_DEX_VIPLEVEL,fname,str,pubkeystr,volAstr,volBstr); - sprintf(fname2,"%s.%llu",fname,(long long)offset0); - bits256_str(hexstr,filehash); - sprintf(volAstr,"%llu",(long long)offset0); - komodo_DEXbroadcast(0,'Q',hexstr,priority+KOMODO_DEX_VIPLEVEL,(char *)"slices",fname,pubkeystr,volAstr,volBstr); - //fprintf(stderr,"broadcast fname.(%s) (%s) filehash.(%s)\n",fname,hexstr,bits256_str(str,filehash)); - } - free(hexstr); - } - fclose(fp), fp = 0; - if ( oldfp != 0 ) - fclose(oldfp), oldfp = 0; - if ( 0 && changed == 0 ) - { - result.push_back(Pair((char *)"result",(char *)"success")); - result.push_back(Pair((char *)"status",(char *)"no changes")); - result.push_back(Pair((char *)"filename",fname)); - result.push_back(Pair((char *)"sliceid",(int64_t)sliceid)); - result.push_back(Pair((char *)"filesize",(int64_t)fsize)); - result.push_back(Pair((char *)"filehash",bits256_str(str,filehash))); - return(result); - } - return(komodo_DEXsubscribe(cmpflag,fname,priority,0,pubkeystr,sliceid)); -} - -UniValue komodo_DEXstream(char *fname,int32_t priority) -{ - static char prevfname[512]; static int32_t prevsliceid; - UniValue result(UniValue::VOBJ); FILE *fp; uint64_t mult,filesize,offset0; char pubkeystr[67],slicefname[512],tagBstr[33]; int32_t sliceid,n; struct DEX_datablob *ptr; - if ( strcmp(prevfname,fname) != 0 ) - { - strcpy(prevfname,fname); - prevsliceid = 1; - } - if ( (fp= fopen(fname,"rb")) != 0 ) - { - fseek(fp,0,SEEK_END); - filesize = ftell(fp); - fclose(fp); - } - else - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"file not found")); - result.push_back(Pair((char *)"filename",fname)); - return(result); - } - mult = KOMODO_DEX_FILEBUFSIZE * KOMODO_DEX_STREAMSIZE; - n = (int32_t)(filesize / mult); - pubkeystr[0] = '0'; - pubkeystr[1] = '1'; - bits256_str(&pubkeystr[2],DEX_pubkey); - for (sliceid=prevsliceid; sliceid<=n+1; sliceid++) - { - offset0 = ((uint64_t)sliceid - 1) * mult; - prevsliceid = sliceid; - sprintf(tagBstr,"%llu",(long long)offset0); - if ( (ptr= _komodo_DEX_latestptr(fname,tagBstr,pubkeystr,0)) == 0 ) - { - //fprintf(stderr,"sliceid.%d cant find (%s/%s) %s\n",sliceid,fname,tagBstr,pubkeystr); - break; - } - sprintf(slicefname,"%s.%llu.%s",fname,(long long)offset0,pubkeystr); - if ( (fp= fopen(slicefname,"rb")) == 0 ) - break; - fseek(fp,0,SEEK_END); - if ( ftell(fp) != mult ) - { - fclose(fp); - break; - } - fclose(fp); - } - if ( sliceid > n+1 ) - sliceid = n+1; - if ( filesize >= offset0+mult ) - return(komodo_DEXpublish(fname,priority,sliceid)); - else - { - result.push_back(Pair((char *)"result",(char *)"success")); - result.push_back(Pair((char *)"warning",(char *)"not enough data to extend stream")); - result.push_back(Pair((char *)"filename",fname)); - result.push_back(Pair((char *)"filesize",(int64_t)filesize)); - result.push_back(Pair((char *)"offset0",(int64_t)offset0)); - result.push_back(Pair((char *)"available",(int64_t)(filesize-offset0))); - result.push_back(Pair((char *)"needed",(int64_t)mult)); - sleep(1); - return(result); - } -} - -FILE *komodo_DEX_streamwrite(char *destfname,FILE *fp,uint64_t wlen,uint64_t offset0) -{ - uint8_t *buf; - buf = (uint8_t *)malloc(wlen); - rewind(fp); - if ( fread(buf,1,wlen,fp) != wlen ) - fprintf(stderr,"error reading %llu slice for %s\n",(long long)wlen,destfname); - fclose(fp); - if ( (fp= fopen(destfname,"rb+")) == 0 ) - fp = fopen(destfname,"wb"); - if ( fp != 0 ) - { - fseek(fp,offset0,SEEK_SET); - if ( fwrite(buf,1,wlen,fp) != wlen ) - fprintf(stderr,"error writing %llu slice to %s\n",(long long)wlen,destfname); - fclose(fp); - fp = 0; - } - free(buf); - return(fp); -} - -int md_unlink(char *file) -{ -#ifdef _WIN32 - _chmod(file, 0600); - return( _unlink(file) ); -#else - return(unlink(file)); -#endif -} - -UniValue komodo_DEXstreamsub(char *fname,int32_t priority,char *pubkeystr) -{ - static char prevfname[512],prevpubkeystr[67]; static int32_t prevsliceid; - UniValue result(UniValue::VOBJ); FILE *fp=0; uint64_t mult,filesize=0,offset0; char slicefname[512],tagBstr[33]; int32_t sliceid,n,cmpflag; struct DEX_datablob *ptr; - if ( pubkeystr == 0 || pubkeystr[0] == 0 || strlen(pubkeystr) > 66 ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"need publisher pubkey for sub")); - return(result); - } - if ( strcmp(prevfname,fname) != 0 || strcmp(prevpubkeystr,pubkeystr) != 0 ) - { - strcpy(prevfname,fname); - strcpy(prevpubkeystr,pubkeystr); - prevsliceid = 1; - } - mult = KOMODO_DEX_FILEBUFSIZE * KOMODO_DEX_STREAMSIZE; - n = (int32_t)(filesize / mult); - for (sliceid=prevsliceid; sliceid<1000*1000; sliceid++) // one TB limit - { - prevsliceid = sliceid; - offset0 = ((uint64_t)sliceid - 1) * mult; - sprintf(slicefname,"%s.%llu.%s",fname,(long long)offset0,pubkeystr); - if ( (fp=fopen(slicefname,"rb")) == 0 ) - break; - else - { - fseek(fp,0,SEEK_END); - if ( (filesize= ftell(fp)) < mult ) - { - if ( filesize > 0 ) - fp = komodo_DEX_streamwrite(fname,fp,filesize,offset0); // eats fp - if ( fp != 0 ) - fclose(fp); - fp = 0; - break; - } - if ( filesize == mult ) - { - result = komodo_DEXsubscribe(cmpflag,fname,priority,0,pubkeystr,sliceid); - if ( cmpflag == 0 ) - { - fclose(fp); - fp = 0; - return(result); - } - else - { - //fprintf(stderr,"streamwrite (%s) offset0.%llu filesize.%llu\n",fname,(long long)offset0,(long long)filesize); - fp = komodo_DEX_streamwrite(fname,fp,filesize,offset0); // eats fp - if ( sliceid >= 2 ) - { - offset0 = ((uint64_t)sliceid - 2) * mult; - sprintf(slicefname,"%s.%llu.%s",fname,(long long)offset0,pubkeystr); - md_unlink(slicefname); - strcat(slicefname,(char *)".locators"); - md_unlink(slicefname); - } - } - } - if ( fp != 0 ) - fclose(fp); - } - } - return(komodo_DEXsubscribe(cmpflag,fname,priority,0,pubkeystr,sliceid)); -} - -int32_t komodo_DEX_anonencode(uint8_t *destbuf,int32_t bufsize,char *hexstr,char *message,bits256 destpub) -{ - bits256 priv0; uint8_t *payload,*payload2,*allocated2=0,*allocated = 0; int32_t i,n,datalen=0; - komodo_DEX_privkey(priv0); - n = (int32_t)strlen(message) + 1; - memcpy(destbuf,message,n); - for (i=n; i> 17) & 0xff; - datalen = bufsize; - if ( (payload= komodo_DEX_encrypt(&allocated,(uint8_t *)message,&datalen,destpub,priv0)) == 0 ) - { - fprintf(stderr,"encryption error for datalen.%d\n",datalen); - datalen = 0; - } - else - { - if ( (payload2= komodo_DEX_encrypt(&allocated2,payload,&datalen,destpub,GENESIS_PRIVKEY)) == 0 ) - { - fprintf(stderr,"encryption error2 for datalen.%d\n",datalen); - datalen = 0; - } - else - { - for (i=0; i KOMODO_DEX_ANONSIZE ) - { - result.push_back(Pair((char *)"result",(char *)"error")); - result.push_back(Pair((char *)"error",(char *)"message too long for anonsend")); - result.push_back(Pair((char *)"maxlength",(int64_t)sizeof(destbuf)-1)); - return(result); - } - decode_hex(destpub.bytes,32,destpub33+2); - komodo_DEX_anonencode(destbuf,KOMODO_DEX_ANONSIZE,hexstr,message,destpub); - pubkeystr[0] = '0'; - pubkeystr[1] = '1'; - bits256_str(pubkeystr+2,GENESIS_PUBKEY); - result = komodo_DEXbroadcast(&locator,'A',hexstr,priority + KOMODO_DEX_CMDPRIORITY,(char *)"anon",(char *)"",pubkeystr,(char *)"",(char *)""); - return(result); -} - -UniValue komodo_DEX_notarize(char *coin,int32_t prevheight) -{ - UniValue result(UniValue::VOBJ); uint8_t *decoded,*buf,*allocated=0,data[512]; int32_t height,n,matches,ntzheight,newlen=0,ind=3,lag; uint32_t t,t2; char pubkeystr[67],str[65],tagB[16]; uint32_t ntztime; int8_t lenA; struct DEX_index *tips[KOMODO_DEX_MAXINDICES]; struct DEX_datablob *ptr; bits256 senderpub,ntzhash,blkhash,hash; - t = (uint32_t)time(NULL); - lenA = (int8_t)strlen(coin); - pubkeystr[0] = '0'; - pubkeystr[1] = '1'; - bits256_str(pubkeystr+2,DEX_pubkey); - { - pthread_mutex_lock(&DEX_globalmutex); - if ( (ptr= _komodo_DEX_latestptr(coin,(char *)"notarizations",pubkeystr,0)) != 0 ) - { - if ( (decoded= komodo_DEX_datablobdecrypt(&senderpub,&allocated,&newlen,ptr,DEX_pubkey,coin)) != 0 && newlen == 40 ) - { - memcpy(ntzhash.bytes,decoded,32); - buf = &decoded[32]; - ntzheight = ((int32_t)buf[3] + ((int32_t)buf[2] << 8) + ((int32_t)buf[1] << 16) + ((int32_t)buf[0] << 24)); - buf += sizeof(ntzheight); - ntztime = ((int32_t)buf[3] + ((int32_t)buf[2] << 8) + ((int32_t)buf[1] << 16) + ((int32_t)buf[0] << 24)); - fprintf(stderr,"last notarization %s.%d (%d) %s t.%u\n",coin,ntzheight,prevheight,bits256_str(str,ntzhash),ntztime); - for (height=ntzheight+1; height> 16) == (1 << ind) ) - fprintf(stderr,"error getting tips for ht.%d ntzheight.%d\n",height,ntzheight); - else if ( tips[ind] != 0 ) - { - memset(blkhash.bytes,0,sizeof(blkhash)); - for (ptr=tips[ind]->tail; ptr!=0; ptr=ptr->prevs[ind]) - { - if ( ptr->cancelled == 0 ) - { - iguana_rwnum(0,&ptr->data[2],sizeof(t2),&t2); - lag = (t - t2); - if ( _komodo_DEX_decryptdata(data,sizeof(data),ptr) == sizeof(hash) ) - { - memcpy(hash.bytes,data,sizeof(hash)); - if ( n == 0 ) - { - blkhash = hash; - matches = 1; - } - else if ( memcmp(blkhash.bytes,hash.bytes,sizeof(blkhash)) == 0 ) - matches++; - n++; - } - } - if ( ptr == tips[ind]->head ) - break; - } - } - if ( n == 0 ) - break; - fprintf(stderr,"%2d: %s ht.%d ntzht.%d matches.%-2d of %2d blockhash %s lag.%d\n",height-ntzheight,coin,height,ntzheight,matches,n,bits256_str(str,blkhash),lag); - } - } - if ( allocated != 0 ) - free(allocated), allocated = 0; - } - //fprintf(stderr,"fname.%s auto search %s %s %s shorthash.%08x sliceid.%d\n",fname,origfname,tagBstr,publisher,shorthash,sliceid); - pthread_mutex_unlock(&DEX_globalmutex); - } - return(result); -} - -void komodo_DEXmsg(CNode *pfrom,std::vector request) // received a packet during interrupt time -{ - int32_t len; std::vector response; bits256 hash; uint32_t timestamp = (uint32_t)time(NULL); - if ( (len= request.size()) > 0 ) - { - pthread_mutex_lock(&DEX_globalmutex); - _komodo_DEXprocess(timestamp,pfrom,&request[0],len); - pthread_mutex_unlock(&DEX_globalmutex); - } -} - -void komodo_DEXpoll(CNode *pto) // from mainloop polling -{ - static uint32_t purgetime; - std::vector packet; uint32_t i,now,numiters,shorthash,len,ptime,modval,peerpos; - now = (uint32_t)time(NULL); - ptime = now - KOMODO_DEX_PURGETIME + 6; - pthread_mutex_lock(&DEX_globalmutex); - peerpos = _komodo_DEXpeerpos(now,pto->id); - if ( ptime > purgetime ) - { - if ( purgetime == 0 ) - purgetime = ptime; - else - { - for (; purgetime pto->dexlastping) || now >= pto->dexlastping+KOMODO_DEX_LOCALHEARTBEAT ) - { - if ( ((now + peerpos) % KOMODO_DEX_POLLVIP) == 0 ) // check the VIP packets - { - numiters = KOMODO_DEX_PURGETIME - KOMODO_DEX_MAXLAG; - pto->dexlastping = now; - } else numiters = KOMODO_DEX_MAXLAG - KOMODO_DEX_MAXHOPS; - for (i=0; i 0 ) - pto->dexlastping = now; - if ( komodo_DEX_islagging() != 0 && i > KOMODO_DEX_MAXLAG ) - break; - } - pto->dexlastping = now; - } - pthread_mutex_unlock(&DEX_globalmutex); -} - diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index b44cf7cf3b2..306a6589c83 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -22,9 +22,6 @@ #include "komodo_defs.h" #include "script/standard.h" #include "cc/CCinclude.h" -#include "cc/CCMarmara.h" - -const char *LOG_KOMODOBITCOIND = "komodostaking"; int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); int32_t komodo_electednotary(int32_t *numnotariesp,uint8_t *pubkey33,int32_t height,uint32_t timestamp); @@ -34,6 +31,8 @@ bool EnsureWalletIsAvailable(bool avoidException); extern bool fRequestShutdown; extern CScript KOMODO_EARLYTXID_SCRIPTPUB; +int32_t MarmaraSignature(uint8_t *utxosig,CMutableTransaction &txNew); +uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32_t &height,int32_t &unlockht); uint32_t komodo_heightstamp(int32_t height); //#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"curl",(char *)"http://127.0.0.1:7776",0,0,(char *)(cmdstr)) @@ -84,20 +83,6 @@ int tx_height( const uint256 &hash ){ return nHeight; } -// hash value to string for logging -static std::string hash2str(arith_uint256 hashval, int nbits) -{ - std::ostringstream os; - - if (nbits < 8) - nbits = 8; - if (nbits > 32) - nbits = 32; - - for (int32_t i = 31; i >= (31 - nbits + 1); i--) - os << std::setw(2) << std::hex << std::setfill('0') << (int)((uint8_t *)&hashval)[i]; - return os.str(); -} /************************************************************************ * @@ -120,8 +105,6 @@ size_t accumulatebytes(void *ptr,size_t size,size_t nmemb,struct return_string * return(size * nmemb); } - - /************************************************************************ * * return the current system time in milliseconds @@ -631,7 +614,6 @@ uint64_t komodo_seed(int32_t height) return(seed); } -// get previous staking utxo opret, address and txtime uint32_t komodo_txtime(CScript &opret,uint64_t *valuep,uint256 hash, int32_t n, char *destaddr) { CTxDestination address; CTransaction tx; uint256 hashBlock; int32_t numvouts; @@ -647,30 +629,12 @@ uint32_t komodo_txtime(CScript &opret,uint64_t *valuep,uint256 hash, int32_t n, } numvouts = tx.vout.size(); //fprintf(stderr,"%s/v%d locktime.%u\n",hash.ToString().c_str(),n,(uint32_t)tx.nLockTime); - - if (n < numvouts) + if ( n < numvouts ) { - bool isCCOpret = false; *valuep = tx.vout[n].nValue; - - if (ASSETCHAINS_MARMARA) - { - // get staking utxo opret - // first try if cc opret exists - if (tx.vout[n].scriptPubKey.IsPayToCryptoCondition()) - { - if (MyGetCCopret(tx.vout[n].scriptPubKey, opret)) - isCCOpret = true; - } - } - if (!isCCOpret) - opret = tx.vout[numvouts-1].scriptPubKey; // if no cc opret then use opret in the last vout - + opret = tx.vout[numvouts-1].scriptPubKey; if (ExtractDestination(tx.vout[n].scriptPubKey, address)) - { - strcpy(destaddr, CBitcoinAddress(address).ToString().c_str()); - LogPrint(LOG_KOMODOBITCOIND, "%s in stake tx found opret and destaddr=%s\n", __func__, destaddr); - } + strcpy(destaddr,CBitcoinAddress(address).ToString().c_str()); } return(tx.nLockTime); } @@ -681,21 +645,6 @@ CBlockIndex *komodo_getblockindex(uint256 hash) return((it != mapBlockIndex.end()) ? it->second : NULL); } -// Extension point to add preferences for stakes (dimxy) -// TODO: what if for some chain several chain's params require different multipliers. Which to select, max? -static int32_t GetStakeMultiplier(CTransaction &tx, int32_t nvout) -{ - int32_t multiplier = 1; // default value - - if (ASSETCHAINS_MARMARA != 0) { - multiplier = MarmaraGetStakeMultiplier(tx, nvout); - } - - CAmount nValue = (nvout >= 0 && nvout < tx.vout.size() ? tx.vout[nvout].nValue : -1); - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG2, stream << "stake multiplier=" << multiplier << " nValue=" << nValue << std::endl); - return multiplier; -} - uint32_t komodo_txtime2(uint64_t *valuep,uint256 hash,int32_t n,char *destaddr) { CTxDestination address; CBlockIndex *pindex; CTransaction tx; uint256 hashBlock; uint32_t txtime = 0; @@ -706,7 +655,7 @@ uint32_t komodo_txtime2(uint64_t *valuep,uint256 hash,int32_t n,char *destaddr) #endif hashBlock, true)) { - fprintf(stderr,"ERROR: %s/v%d locktime.%u\n",hash.ToString().c_str(),n,(uint32_t)tx.nLockTime); // TODO: comment + //fprintf(stderr,"ERROR: %s/v%d locktime.%u\n",hash.ToString().c_str(),n,(uint32_t)tx.nLockTime); return(0); } if ( (pindex= komodo_getblockindex(hashBlock)) != 0 ) @@ -715,9 +664,7 @@ uint32_t komodo_txtime2(uint64_t *valuep,uint256 hash,int32_t n,char *destaddr) //fprintf(stderr,"%s/v%d locktime.%u\n",hash.ToString().c_str(),n,(uint32_t)tx.nLockTime); if ( n < tx.vout.size() ) { - int32_t stakemultiplier = GetStakeMultiplier(tx, n); - *valuep = tx.vout[n].nValue * stakemultiplier; - + *valuep = tx.vout[n].nValue; if (ExtractDestination(tx.vout[n].scriptPubKey, address)) strcpy(destaddr,CBitcoinAddress(address).ToString().c_str()); } @@ -752,9 +699,7 @@ int32_t komodo_newStakerActive(int32_t height, uint32_t timestamp) int32_t komodo_hasOpRet(int32_t height, uint32_t timestamp) { - // dimxy: marmara now has ccopret for staking, so vout num = 1 for marmara staking txns - // only old marmara testers MCL0 chain does have vout=2 but it should not be used any more - return((/* ASSETCHAINS_MARMARA!=0 || */ komodo_newStakerActive(height, timestamp) == 1)); + return((ASSETCHAINS_MARMARA!=0 || komodo_newStakerActive(height, timestamp) == 1)); } bool komodo_checkopret(CBlock *pblock, CScript &merkleroot) @@ -768,6 +713,8 @@ bool komodo_hardfork_active(uint32_t time) return ( (ASSETCHAINS_SYMBOL[0] == 0 && chainActive.Height() > nDecemberHardforkHeight) || (ASSETCHAINS_SYMBOL[0] != 0 && time > nStakedDecemberHardforkTimestamp) ); //December 2019 hardfork } +bool MarmaraPoScheck(char *destaddr,CScript opret,CTransaction staketx); + uint256 komodo_calcmerkleroot(CBlock *pblock, uint256 prevBlockHash, int32_t nHeight, bool fNew, CScript scriptPubKey) { std::vector vLeaves; @@ -790,37 +737,29 @@ uint256 komodo_calcmerkleroot(CBlock *pblock, uint256 prevBlockHash, int32_t nHe return GetMerkleRoot(vLeaves); } - -// checks if block is PoS: -// last tx should point to the previous staking utxo (that is spent to self) -// for Marmara cc there is an additional check of staking tx (opret) -// returns 1 if this is PoS block and 0 if false int32_t komodo_isPoS(CBlock *pblock, int32_t height,CTxDestination *addressout) { - int32_t n,vout,numvouts,ret; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid, merkleroot; CScript prevTxOpret; + int32_t n,vout,numvouts,ret; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid, merkleroot; CScript opret; if ( ASSETCHAINS_STAKED != 0 ) { n = pblock->vtx.size(); - - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "ht." << height << " check for PoS numtx." << n << " numvins." << pblock->vtx[n-1].vin.size() << " numvouts." << pblock->vtx[n-1].vout.size() << std::endl); + //fprintf(stderr,"ht.%d check for PoS numtx.%d numvins.%d numvouts.%d\n",height,n,(int32_t)pblock->vtx[n-1].vin.size(),(int32_t)pblock->vtx[n-1].vout.size()); if ( n > 1 && pblock->vtx[n-1].vin.size() == 1 && pblock->vtx[n-1].vout.size() == 1+komodo_hasOpRet(height,pblock->nTime) ) { - // get previous tx and check if it was spent to self - txid = pblock->vtx[n-1].vin[0].prevout.hash; + txid = pblock->vtx[n-1].vin[0].prevout.hash; vout = pblock->vtx[n-1].vin[0].prevout.n; - txtime = komodo_txtime(prevTxOpret, &value, txid, vout, destaddr); // get previous stake tx opret and addr - if ( ExtractDestination(pblock->vtx[n-1].vout[0].scriptPubKey,voutaddress) ) // get current tx vout address + txtime = komodo_txtime(opret,&value,txid,vout,destaddr); + if ( ExtractDestination(pblock->vtx[n-1].vout[0].scriptPubKey,voutaddress) ) { if ( addressout != 0 ) *addressout = voutaddress; strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str()); - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG2, stream << "check voutaddr." << voutaddr << " vs prevtx destaddr." << destaddr << std::endl); - if ( komodo_newStakerActive(height, pblock->nTime) != 0 ) + //fprintf(stderr,"voutaddr.%s vs destaddr.%s\n",voutaddr,destaddr); + if ( komodo_newStakerActive(height, pblock->nTime) != 0 ) { if ( DecodeStakingOpRet(pblock->vtx[n-1].vout[1].scriptPubKey, merkleroot) != 0 && komodo_calcmerkleroot(pblock, pblock->hashPrevBlock, height, false, pblock->vtx[0].vout[0].scriptPubKey) == merkleroot ) { return(1); } - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << "ht=" << height << " incorrect stake tx (komodo_calcmerkleroot failed)" << std::endl); } else { @@ -830,30 +769,21 @@ int32_t komodo_isPoS(CBlock *pblock, int32_t height,CTxDestination *addressout) return(1); else { - // marmara code: - // MarmaraValidateStakeTx does all required checks for stake tx: - int32_t marmara_validate_staketx = MarmaraValidateStakeTx(destaddr, prevTxOpret, pblock->vtx[n - 1], height); - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "ht=" << height << " MarmaraValidateStakeTx returned=" << marmara_validate_staketx << std::endl); - return marmara_validate_staketx; - // end marmara code + if ( pblock->vtx[n-1].vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 && (numvouts= pblock->vtx[n-1].vout.size()) == 2 ) + { + //fprintf(stderr,"validate proper %s %s signature and unlockht preservation\n",voutaddr,destaddr); + return(MarmaraPoScheck(destaddr,opret,pblock->vtx[n-1])); + } + else + { + fprintf(stderr,"reject ht.%d PoS block\n",height); + return(strcmp(ASSETCHAINS_SYMBOL,"MTST2") == 0); // allow until MTST3 + } } } - else - { - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "ht=" << height << " incorrect stake tx (value or destaddr not matched)" << std::endl); - } } } - else - { - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "ht=" << height << " not a stake tx (couldn't ExtractDestination)" << std::endl); - } } - else - { - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "ht=" << height << " not a stake tx (n, vin.size or vout.size not matched)" << std::endl); - } - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "ht=" << height << " return=0" << std::endl); } return(0); } @@ -1471,13 +1401,10 @@ uint32_t komodo_segid32(char *coinaddr) int8_t komodo_segid(int32_t nocache,int32_t height) { CTxDestination voutaddress; CBlock block; CBlockIndex *pindex; uint64_t value; uint32_t txtime; char voutaddr[64],destaddr[64]; int32_t txn_count,vout,newStakerActive; uint256 txid,merkleroot; CScript opret; int8_t segid = -1; - if ( height > 0 && (pindex= komodo_chainactive(height)) != 0 ) { - if (nocache == 0 && pindex->segid >= -1) { - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "return cached segid, height." << height << " -> " << (int)pindex->segid << std::endl); // uncommented + if ( nocache == 0 && pindex->segid >= -1 ) return(pindex->segid); - } if ( komodo_blockload(block,pindex) == 0 ) { newStakerActive = komodo_newStakerActive(height, block.nTime); @@ -1496,7 +1423,6 @@ int8_t komodo_segid(int32_t nocache,int32_t height) { segid = komodo_segid32(voutaddr) & 0x3f; //fprintf(stderr, "komodo_segid: ht.%i --> %i\n",height,pindex->segid); - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "set calculated segid, height." << height << " -> " << (int)pindex->segid << std::endl); // uncommented } } //else fprintf(stderr,"komodo_segid ht.%d couldnt extract voutaddress\n",height); } @@ -1506,13 +1432,6 @@ int8_t komodo_segid(int32_t nocache,int32_t height) if ( pindex->segid == -2 ) pindex->segid = segid; } - - else - { - // fprintf(stderr, "%s pindex==null ht.%d default value=%d\n", __func__, height, segid); - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "pindex==null ht." << height << " segid default value=" << (int)segid << std::endl); - } - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG2, stream << "ht." << height << " return segid=" << (int)segid << std::endl); return(segid); } @@ -1578,7 +1497,7 @@ arith_uint256 komodo_adaptivepow_target(int32_t height,arith_uint256 bnTarget,ui arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc,int32_t newStakerActive) { - int32_t oldflag = 0, dispflag = 0 /*set to 1 to debug segids*/; + int32_t oldflag = 0,dispflag = 0; CBlockIndex *pindex; arith_uint256 easydiff,bnTarget,hashval,sum,ave; bool fNegative,fOverflow; int32_t i,n,m,ht,percPoS,diff,val; *percPoSp = percPoS = 0; @@ -1595,8 +1514,6 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he } else easydiff.SetCompact(STAKING_MIN_DIFF,&fNegative,&fOverflow); - - std::ostringstream strSegids; // for logging segids flags for (i=n=m=0; i<100; i++) { ht = height - 100 + i; @@ -1608,22 +1525,20 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he { n++; percPoS++; - if (dispflag != 0 && ASSETCHAINS_STAKED < 100) - strSegids << "0"; // fprintf(stderr,"0"); - not to contaminate 00011100110 output with deeper printing + if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 ) + fprintf(stderr,"0"); } else { - if (dispflag != 0 && ASSETCHAINS_STAKED < 100) - strSegids << "1"; // fprintf(stderr,"1"); - not to contaminate 00011100110 output with deeper printing + if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 ) + fprintf(stderr,"1"); sum += UintToArith256(pindex->GetBlockHash()); m++; } } //else fprintf(stderr, "pindex returned null ht.%i\n",ht); - if (dispflag != 0 && ASSETCHAINS_STAKED < 100 && (i % 10) == 9) - strSegids << " " << percPoS << " "; // fprintf(stderr," %d, ", percPoS); + if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 && (i % 10) == 9 ) + fprintf(stderr," %d, ",percPoS); } - if (dispflag != 0 && ASSETCHAINS_STAKED < 100) - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_INFO, stream << strSegids.str() << std::endl); if ( m+n < 100 ) { // We do actual PoS % at the start. Requires coin distribution in first 10 blocks! @@ -1633,8 +1548,7 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he percPoS = ((percPoS * n) + (goalperc * (100-n))) / 100; } if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 ) - LogPrint(LOG_KOMODOBITCOIND," -> %d%% percPoS vs goalperc.%d ht.%d\n",percPoS,goalperc,height); - + fprintf(stderr," -> %d%% percPoS vs goalperc.%d ht.%d\n",percPoS,goalperc,height); *percPoSp = percPoS; if ( m > 0 ) { @@ -1700,21 +1614,20 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeight,uint256 txid,int32_t vout,uint32_t blocktime,uint32_t prevtime,char *destaddr,int32_t PoSperc) { - bool fNegative,fOverflow; uint8_t hashbuf[256]; char address[64]; bits256 addrhash; arith_uint256 hashval,mindiff,ratio,coinage256; uint256 hash,pasthash; int32_t segid,minage,iter=0; int64_t diff=0; uint32_t txtime,segid32,winner = 0 ; uint64_t value,coinage; + bool fNegative,fOverflow; uint8_t hashbuf[256]; char address[64]; bits256 addrhash; arith_uint256 hashval,mindiff,ratio,coinage256; uint256 hash,pasthash; int32_t segid,minage,i,iter=0; int64_t diff=0; uint32_t txtime,segid32,winner = 0 ; uint64_t value,coinage; txtime = komodo_txtime2(&value,txid,vout,address); if ( validateflag == 0 ) { //fprintf(stderr,"blocktime.%u -> ",blocktime); if ( blocktime < prevtime+3 ) blocktime = prevtime+3; - if ( blocktime < GetAdjustedTime()-60 ) - blocktime = GetAdjustedTime()+30; + if ( blocktime < GetTime()-60 ) + blocktime = GetTime()+30; //fprintf(stderr,"blocktime.%u txtime.%u\n",blocktime,txtime); } if ( value == 0 || txtime == 0 || blocktime == 0 || prevtime == 0 ) { //fprintf(stderr,"komodo_stake null %.8f %u %u %u\n",dstr(value),txtime,blocktime,prevtime); - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "null value=" << value << " txtime=" << txtime << " blocktime=" << blocktime << " prevtime=" << prevtime << std::endl); return(0); } if ( value < SATOSHIDEN ) @@ -1727,7 +1640,6 @@ uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeigh komodo_segids(hashbuf,nHeight-101,100); segid32 = komodo_stakehash(&hash,address,hashbuf,txid,vout); segid = ((nHeight + segid32) & 0x3f); - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "segid=" << segid << " address=" << address << std::endl); for (iter=0; iter<600; iter++) { if ( blocktime+iter+segid*2 < txtime+minage ) @@ -1796,21 +1708,18 @@ uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeigh for (i=31; i>=24; i--) fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff); */ - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_INFO, stream << "block not validated as PoS:" << " hashval=" << hash2str(hashval, 8) << " > bnTarget=" << hash2str(bnTarget, 8) << " iter=" << iter << " winner=" << winner << " segid=" << segid << " coinage=" << coinage << " blocktime-txtime=" << blocktime - txtime << " ht=" << nHeight << " blocktime=" << blocktime << " value=" << value << " diff=" << diff << std::endl); break; } } - //fprintf(stderr,"iterated until iter.%d winner.%d value=%lld segid=%d validateflag=%d\n",iter,winner, value, segid, validateflag); - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG2, stream << "iterated until iter=" << iter << " winner=" << winner << " value=" << value << " segid=" << segid << " validateflag=" << validateflag << std::endl); - if ( false && validateflag != 0 ) + //fprintf(stderr,"iterated until i.%d winner.%d\n",i,winner); + if ( 0 && validateflag != 0 ) { - /*for (int32_t i=31; i>=24; i--) + for (i=31; i>=24; i--) fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]); fprintf(stderr," vs "); - for (int32_t i=31; i>=24; i--) - fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]);*/ - //fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d ht.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff,nHeight); - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_INFO, stream << "hashval=" << hash2str(hashval, 8) << " vs bnTarget=" << hash2str(bnTarget, 8) << "segid=" << segid << " iter=" << iter << " winner=" << winner << " coinage=" << coinage << " blocktime-txtime=" << blocktime - txtime << " ht=" << nHeight << " blocktime=" << blocktime << " value=" << value << " diff=" << diff << std::endl); + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); + fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d ht.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff,nHeight); } if ( nHeight < 10 ) return(blocktime); @@ -1836,8 +1745,7 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ return(komodo_isPoS(pblock,height,0)); } txn_count = pblock->vtx.size(); - - // crashes RICK? CCLogPrintF(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, "%s checkblock ht.%d block=%s n.%d vins.%d vouts.%d vout[0].%.8f\n", __func__, height, pblock->GetHash().GetHex().c_str(), txn_count, (int32_t)pblock->vtx[txn_count-1].vin.size(), (int32_t)pblock->vtx[txn_count-1].vout.size(), (double)pblock->vtx[txn_count-1].vout[0].nValue/COIN/*, (double)pblock->vtx[txn_count-1].vout[1].nValue/COIN*/); //uncommented + //fprintf(stderr,"checkblock n.%d vins.%d vouts.%d %.8f %.8f\n",txn_count,(int32_t)pblock->vtx[txn_count-1].vin.size(),(int32_t)pblock->vtx[txn_count-1].vout.size(),(double)pblock->vtx[txn_count-1].vout[0].nValue/COIN,(double)pblock->vtx[txn_count-1].vout[1].nValue/COIN); if ( txn_count > 1 && pblock->vtx[txn_count-1].vin.size() == 1 && pblock->vtx[txn_count-1].vout.size() == 1+komodo_hasOpRet(height,pblock->nTime) ) { it = mapBlockIndex.find(pblock->hashPrevBlock); @@ -1852,16 +1760,11 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ { // checks utxo is eligible to stake this block eligible = komodo_stake(1,bnTarget,height,txid,vout,pblock->nTime,prevtime+ASSETCHAINS_STAKED_BLOCK_FUTURE_HALF,(char *)"",PoSperc); - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << " eligible=" << eligible << " pblock->nTime=" << pblock->nTime << std::endl); - } - else - { - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << " komodo_isPoS ht=" << height << " block=" << pblock->GetHash().GetHex() << " returned false" << std::endl); } if ( eligible == 0 || eligible > pblock->nTime ) { - if ( false && ASSETCHAINS_STAKED < 100 ) - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << " PoS failure ht." << height << " eligible." << eligible << " vs blocktime." << pblock->nTime << " lag." << (eligible - pblock->nTime) << " -> check to see if it is PoW block" << std::endl); + if ( 0 && ASSETCHAINS_STAKED < 100 ) + fprintf(stderr,"komodo_is_PoSblock PoS failure ht.%d eligible.%u vs blocktime.%u, lag.%d -> check to see if it is PoW block\n",height,eligible,(uint32_t)pblock->nTime,(int32_t)(eligible - pblock->nTime)); } else { @@ -1874,13 +1777,13 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ */ if ( ASSETCHAINS_STAKED < 100 && bhash < POWTarget ) { - CCLogPrintF(LOG_KOMODOBITCOIND, CCLOG_ERROR, "[%s:%i] isPoS but meets PoW diff nBits.%u < target.%u\n", ASSETCHAINS_SYMBOL, height, bhash.GetCompact(), POWTarget.GetCompact()); + fprintf(stderr,"[%s:%i] isPoS but meets PoW diff nBits.%u < target.%u\n", ASSETCHAINS_SYMBOL, height, bhash.GetCompact(), POWTarget.GetCompact()); isPoS = 0; } } } } - fprintf(stderr,"%s slow.%d ht.%d isPoS.%d\n", __func__,slowflag,height,isPoS); //uncommented + //fprintf(stderr,"slow.%d ht.%d isPoS.%d\n",slowflag,height,isPoS); return(isPoS != 0); } @@ -2401,11 +2304,10 @@ int32_t komodo_checkPOW(int64_t stakeTxValue, int32_t slowflag,CBlock *pblock,in KOMODO_TEST_ASSETCHAIN_SKIP_POW = 1; if ( !CheckEquihashSolution(pblock, Params()) ) { - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << "slowflag." << slowflag << " ht." << height << " CheckEquihashSolution failed" << std::endl); + fprintf(stderr,"komodo_checkPOW slowflag.%d ht.%d CheckEquihashSolution failed\n",slowflag,height); return(-1); } hash = pblock->GetHash(); - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_DEBUG1, stream << "checking block." << hash.GetHex() << " ht." << height << " bits." << pblock->nBits << std::endl); bnTarget.SetCompact(pblock->nBits,&fNegative,&fOverflow); bhash = UintToArith256(hash); possible = komodo_block2pubkey33(pubkey33,pblock); @@ -2413,7 +2315,7 @@ int32_t komodo_checkPOW(int64_t stakeTxValue, int32_t slowflag,CBlock *pblock,in { if ( slowflag != 0 ) { - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_INFO, stream << "height." << height << " slowflag." << slowflag << " possible."<< possible << " (is bhash > bnTarget)=" << (bhash > bnTarget) << std::endl); + fprintf(stderr,"height.%d slowflag.%d possible.%d cmp.%d\n",height,slowflag,possible,bhash > bnTarget); return(0); } BlockMap::const_iterator it = mapBlockIndex.find(pblock->hashPrevBlock); @@ -2473,7 +2375,12 @@ int32_t komodo_checkPOW(int64_t stakeTxValue, int32_t slowflag,CBlock *pblock,in bnTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED,newStakerActive); if ( bhash > bnTarget ) { - LOGSTREAM(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << "[" << ASSETCHAINS_SYMBOL << ":" << height << "]" << " bnhash=" << hash2str(bhash, 16) << " > bnTarget=" << hash2str(bnTarget, 16) << " ht." << height << " PoW diff violation PoSperc." << PoSperc << " vs goalperc." << (int)ASSETCHAINS_STAKED << std::endl); + for (i=31; i>=16; i--) + fprintf(stderr,"%02x",((uint8_t *)&bhash)[i]); + fprintf(stderr," > "); + for (i=31; i>=16; i--) + fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); + fprintf(stderr," ht.%d PoW diff violation PoSperc.%d vs goalperc.%d\n",height,PoSperc,(int32_t)ASSETCHAINS_STAKED); return(-1); } else @@ -2484,7 +2391,6 @@ int32_t komodo_checkPOW(int64_t stakeTxValue, int32_t slowflag,CBlock *pblock,in if ( pblock->vtx.size() > 1 && pblock->vtx[pblock->vtx.size()-1].vout.size() == 2 && DecodeStakingOpRet(pblock->vtx[pblock->vtx.size()-1].vout[1].scriptPubKey, merkleroot) != 0 ) { fprintf(stderr, "[%s:%d] staking tx in PoW block, nBits.%u < target.%u\n", ASSETCHAINS_SYMBOL,height,bhash.GetCompact(),bnTarget.GetCompact()); - LOGSTREAM(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << "[" << ASSETCHAINS_SYMBOL << ":" << height << "]" << " staking tx found in PoW block, nBits=" << bhash.GetCompact() << " < target=" << bnTarget.GetCompact() << std::endl); return(-1); } // set the pindex->segid as this is now fully validated to be a PoW block. @@ -2500,8 +2406,7 @@ int32_t komodo_checkPOW(int64_t stakeTxValue, int32_t slowflag,CBlock *pblock,in } else if ( is_PoSblock < 0 ) { - //fprintf(stderr,"[%s:%d] unexpected negative is_PoSblock.%d\n",ASSETCHAINS_SYMBOL,height,is_PoSblock); - LOGSTREAM(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << "[" << ASSETCHAINS_SYMBOL << ":" << height << "]" << " unexpected negative is_PoSblock." << is_PoSblock << std::endl); + fprintf(stderr,"[%s:%d] unexpected negative is_PoSblock.%d\n",ASSETCHAINS_SYMBOL,height,is_PoSblock); return(-1); } else @@ -2513,14 +2418,12 @@ int32_t komodo_checkPOW(int64_t stakeTxValue, int32_t slowflag,CBlock *pblock,in // the coinbase must pay the fees from the last transaction and the block reward at a minimum. if ( pblock->vtx.size() < 1 || pblock->vtx[0].vout.size() < 1 ) { - //fprintf(stderr, "[%s:%d] missing coinbase.\n", ASSETCHAINS_SYMBOL, height); - LOGSTREAM(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << "[" << ASSETCHAINS_SYMBOL << ":" << height << "]" << " missing coinbase.\n"); + fprintf(stderr, "[%s:%d] missing coinbase.\n", ASSETCHAINS_SYMBOL, height); return(-1); } else if ( pblock->vtx[0].vout[0].nValue < stakeTxValue ) { - //fprintf(stderr, "[%s:%d] coinbase vout0.%lld < blockreward+stakingtxfee.%lld\n", ASSETCHAINS_SYMBOL, height, (long long)pblock->vtx[0].vout[0].nValue, (long long)stakeTxValue); - LOGSTREAM(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << "[" << ASSETCHAINS_SYMBOL << ":" << height << "]" << " coinbase vout0." << (long long)pblock->vtx[0].vout[0].nValue << " < blockreward+stakingtxfee." << (long long)stakeTxValue << std::endl); + fprintf(stderr, "[%s:%d] coinbase vout0.%lld < blockreward+stakingtxfee.%lld\n", ASSETCHAINS_SYMBOL, height, (long long)pblock->vtx[0].vout[0].nValue, (long long)stakeTxValue); return(-1); } // set the pindex->segid as this is now fully validated to be a PoS block. @@ -2581,8 +2484,7 @@ int32_t komodo_checkPOW(int64_t stakeTxValue, int32_t slowflag,CBlock *pblock,in // Check the notarisation tx is to the crypto address. if ( !komodo_is_notarytx(pblock->vtx[1]) == 1 ) { - //fprintf(stderr, "notarisation is not to crypto address ht.%i\n",height); - LOGSTREAM(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << "[" << ASSETCHAINS_SYMBOL << ":" << height << "]" << " notarisation is not to crypto address ht." << height << std::endl); + fprintf(stderr, "notarisation is not to crypto address ht.%i\n",height); return(-1); } // Check min sigs. @@ -2590,8 +2492,7 @@ int32_t komodo_checkPOW(int64_t stakeTxValue, int32_t slowflag,CBlock *pblock,in numSN = komodo_notaries(notarypubkeys, height, pblock->nTime); if ( pblock->vtx[1].vin.size() < numSN/5 ) { - // fprintf(stderr, "ht.%i does not meet minsigs.%i sigs.%lld\n",height,numSN/5,(long long)pblock->vtx[1].vin.size()); - LOGSTREAM(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << "[" << ASSETCHAINS_SYMBOL << ":" << height << "]" << " ht." << height << " does not meet minsigs." << numSN / 5 << " sigs." << (long long)pblock->vtx[1].vin.size() << std::endl); + fprintf(stderr, "ht.%i does not meet minsigs.%i sigs.%lld\n",height,numSN/5,(long long)pblock->vtx[1].vin.size()); return(-1); } } @@ -2690,7 +2591,7 @@ int64_t komodo_coinsupply(int64_t *zfundsp,int64_t *sproutfundsp,int32_t height) pindex->newcoins = komodo_newcoins(&pindex->zfunds,&pindex->sproutfunds,pindex->GetHeight(),&block); else { - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << "error loading block ht=" << pindex->GetHeight() << std::endl); + fprintf(stderr,"error loading block.%d\n",pindex->GetHeight()); return(0); } } @@ -2705,22 +2606,25 @@ int64_t komodo_coinsupply(int64_t *zfundsp,int64_t *sproutfundsp,int32_t height) *sproutfundsp = sproutfunds; return(supply); } - +struct komodo_staking +{ + char address[64]; + uint256 txid; + arith_uint256 hashval; + uint64_t nValue; + uint32_t segid32,txtime; + int32_t vout; + CScript scriptPubKey; +}; struct komodo_staking *komodo_addutxo(struct komodo_staking *array,int32_t *numkp,int32_t *maxkp,uint32_t txtime,uint64_t nValue,uint256 txid,int32_t vout,char *address,uint8_t *hashbuf,CScript pk) { uint256 hash; uint32_t segid32; struct komodo_staking *kp; - segid32 = komodo_stakehash(&hash,address,hashbuf,txid,vout); if ( *numkp >= *maxkp ) { *maxkp += 1000; - struct komodo_staking *newarray = (struct komodo_staking *)realloc(array,sizeof(*array) * (*maxkp)); - if (newarray == NULL) { - fprintf(stderr, "%s could not allocate memory\n", __func__); - return array; // prevent buf overflow, do not add utxo if no more mem allocated - } - array = newarray; + array = (struct komodo_staking *)realloc(array,sizeof(*array) * (*maxkp)); //fprintf(stderr,"realloc max.%d array.%p\n",*maxkp,array); } kp = &array[(*numkp)++]; @@ -2745,9 +2649,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt uint64_t cbPerc = *utxovaluep, tocoinbase = 0; if (!EnsureWalletIsAvailable(0)) return 0; - - const bool needSpecialStakeUtxo = (ASSETCHAINS_MARMARA != 0); //conditions of contracts or params for which non-basic utxo for staking are needed - + bnTarget.SetCompact(nBits, &fNegative, &fOverflow); assert(pwalletMain != NULL); *utxovaluep = 0; @@ -2770,9 +2672,9 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt LOCK(cs_main); CBlockIndex* pblockindex = chainActive[tipindex->GetHeight()]; CBlock block; CTxDestination addressout; - if (needSpecialStakeUtxo) + if ( ASSETCHAINS_MARMARA != 0 ) resetstaker = true; - else if ( ReadBlockFromDisk(block, pblockindex, 1) && komodo_isPoS(&block, nHeight, &addressout) != 0 && IsMine(*pwalletMain,addressout) != 0 ) + else if ( ReadBlockFromDisk(block, pblockindex, 1) && komodo_isPoS(&block, nHeight, &addressout) != 0 && IsMine(*pwalletMain,addressout) != 0 ) { resetstaker = true; fprintf(stderr, "[%s:%d] Reset ram staker after mining a block!\n",ASSETCHAINS_SYMBOL,nHeight); @@ -2790,9 +2692,8 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt maxkp = numkp = 0; lasttime = 0; } - if (!needSpecialStakeUtxo) + if ( ASSETCHAINS_MARMARA == 0 ) { - // add normal staking UTXO: BOOST_FOREACH(const COutput& out, vecOutputs) { if ( (tipindex= chainActive.Tip()) == 0 || tipindex->GetHeight()+1 > nHeight ) @@ -2822,18 +2723,36 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt } } } - else + else { - // placeholder for special staking utxo cases: - // marmara case: - if (ASSETCHAINS_MARMARA != 0) { - array = MarmaraGetStakingUtxos(array, &numkp, &maxkp, hashbuf); + struct CCcontract_info *cp,C; uint256 txid; int32_t vout,ht,unlockht; CAmount nValue; char coinaddr[64]; CPubKey mypk,Marmarapk,pk; + std::vector > unspentOutputs; + cp = CCinit(&C,EVAL_MARMARA); + mypk = pubkey2pk(Mypubkey()); + Marmarapk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,Marmarapk,mypk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( (nValue= it->second.satoshis) < COIN ) + continue; + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + { + const CScript &scriptPubKey = tx.vout[vout].scriptPubKey; + if ( DecodeMaramaraCoinbaseOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,pk,ht,unlockht) != 0 && pk == mypk ) + { + array = komodo_addutxo(array,&numkp,&maxkp,(uint32_t)pindex->nTime,(uint64_t)nValue,txid,vout,coinaddr,hashbuf,(CScript)scriptPubKey); + } + // else fprintf(stderr,"SKIP addutxo %.8f numkp.%d vs max.%d\n",(double)nValue/COIN,numkp,maxkp); + } } } lasttime = (uint32_t)time(NULL); //fprintf(stderr,"finished kp data of utxo for staking %u ht.%d numkp.%d maxkp.%d\n",(uint32_t)time(NULL),nHeight,numkp,maxkp); } - block_from_future_rejecttime = (uint32_t)GetAdjustedTime() + ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX; + block_from_future_rejecttime = (uint32_t)GetTime() + ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX; for (i=winners=0; i 0) + siglen = MarmaraSignature(utxosig,txNew); + if ( siglen > 0 ) signSuccess = true; - else - signSuccess = false; + else signSuccess = false; } if (!signSuccess) - LOGSTREAMFN(LOG_KOMODOBITCOIND, CCLOG_ERROR, stream << "failed to create signature\n"); + fprintf(stderr,"failed to create signature\n"); else *blocktimep = earliest; } diff --git a/src/komodo_cJSON.c b/src/komodo_cJSON.c index f5d4dc22340..10140ad0232 100644 --- a/src/komodo_cJSON.c +++ b/src/komodo_cJSON.c @@ -557,4 +557,3 @@ cJSON *addrs_jsonarray(uint64_t *addrs,int32_t num) } void free_json(cJSON *json) { if ( json != 0 ) cJSON_Delete(json); } - diff --git a/src/komodo_defs.h b/src/komodo_defs.h index ac039379914..747559dd0e7 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -15,8 +15,6 @@ #ifndef KOMODO_DEFS_H #define KOMODO_DEFS_H - -#include #include "arith_uint256.h" #include "chain.h" #include "komodo_nk.h" @@ -40,8 +38,6 @@ #define ASSETCHAINS_STAKED_MIN_POW_DIFF 536900000 // 537000000 537300000 #define _COINBASE_MATURITY 100 -#define KOMODO_ADDRESS_BUFSIZE 64 - // KMD Notary Seasons // 1: May 1st 2018 1530921600 // 2: July 15th 2019 1563148800 -> estimated height 1444000 @@ -50,14 +46,17 @@ // 7113400 = 5x current KMD blockheight. // to add 4th season, change NUM_KMD_SEASONS to 4, and add timestamp and height of activation to these arrays. -#define NUM_KMD_SEASONS 4 +#define NUM_KMD_SEASONS 5 #define NUM_KMD_NOTARIES 64 extern const uint32_t nStakedDecemberHardforkTimestamp; //December 2019 hardfork extern const int32_t nDecemberHardforkHeight; //December 2019 hardfork -static const uint32_t KMD_SEASON_TIMESTAMPS[NUM_KMD_SEASONS] = {1525132800, 1563148800, nStakedDecemberHardforkTimestamp, 1751328000}; -static const int32_t KMD_SEASON_HEIGHTS[NUM_KMD_SEASONS] = {814000, 1444000, nDecemberHardforkHeight, 7113400}; +extern const uint32_t nS4Timestamp; //dPoW Season 4 2020 hardfork +extern const int32_t nS4HardforkHeight; //dPoW Season 4 2020 hardfork + +static const uint32_t KMD_SEASON_TIMESTAMPS[NUM_KMD_SEASONS] = {1525132800, 1563148800, nStakedDecemberHardforkTimestamp, nS4Timestamp, 1751328000}; +static const int32_t KMD_SEASON_HEIGHTS[NUM_KMD_SEASONS] = {814000, 1444000, nDecemberHardforkHeight, nS4HardforkHeight, 7113400}; // Era array of pubkeys. Add extra seasons to bottom as requried, after adding appropriate info above. static const char *notaries_elected[NUM_KMD_SEASONS][NUM_KMD_NOTARIES][2] = @@ -326,7 +325,74 @@ static const char *notaries_elected[NUM_KMD_SEASONS][NUM_KMD_NOTARIES][2] = {"gt_AR", "0348430538a4944d3162bb4749d8c5ed51299c2434f3ee69c11a1f7815b3f46135" }, {"patchkez_SH", "03f45e9beb5c4cd46525db8195eb05c1db84ae7ef3603566b3d775770eba3b96ee" }, {"decker_AR", "03ffdf1a116300a78729608d9930742cd349f11a9d64fcc336b8f18592dd9c91bc" }, // 63 - } + }, + { + // Season 4 + { "alien_AR", "03911a60395801082194b6834244fa78a3c30ff3e888667498e157b4aa80b0a65f" }, + { "alien_EU", "03bb749e337b9074465fa28e757b5aa92cb1f0fea1a39589bca91a602834d443cd" }, + { "strob_NA", "02a1c0bd40b294f06d3e44a52d1b2746c260c475c725e9351f1312e49e01c9a405" }, + { "titomane_SH", "020014ad4eedf6b1aeb0ad3b101a58d0a2fc570719e46530fd98d4e585f63eb4ae" }, + { "fullmoon_AR", "03b251095e747f759505ec745a4bbff9a768b8dce1f65137300b7c21efec01a07a" }, + { "phba2061_EU", "03a9492d2a1601d0d98cfe94d8adf9689d1bb0e600088127a4f6ca937761fb1c66" }, + { "fullmoon_NA", "03931c1d654a99658998ce0ddae108d825943a821d1cddd85e948ac1d483f68fb6" }, + { "fullmoon_SH", "03c2a1ed9ddb7bb8344328946017b9d8d1357b898957dd6aaa8c190ae26740b9ff" }, + { "madmax_AR", "022be5a2829fa0291f9a51ff7aeceef702eef581f2611887c195e29da49092e6de" }, + { "titomane_EU", "0285cf1fdba761daf6f1f611c32d319cd58214972ef822793008b69dde239443dd" }, + { "cipi_NA", "022c6825a24792cc3b010b1531521eba9b5e2662d640ed700fd96167df37e75239" }, + { "indenodes_SH", "0334e6e1ec8285c4b85bd6dae67e17d67d1f20e7328efad17ce6fd24ae97cdd65e" }, + { "decker_AR", "03ffdf1a116300a78729608d9930742cd349f11a9d64fcc336b8f18592dd9c91bc" }, + { "indenodes_EU", "0221387ff95c44cb52b86552e3ec118a3c311ca65b75bf807c6c07eaeb1be8303c" }, + { "madmax_NA", "02997b7ab21b86bbea558ae79acc35d62c9cedf441578f78112f986d72e8eece08" }, + { "chainzilla_SH", "02288ba6dc57936b59d60345e397d62f5d7e7d975f34ed5c2f2e23288325661563" }, + { "peer2cloud_AR", "0250e7e43a3535731b051d1bcc7dc88fbb5163c3fe41c5dee72bd973bcc4dca9f2" }, + { "pirate_EU", "0231c0f50a06655c3d2edf8d7e722d290195d49c78d50de7786b9d196e8820c848" }, + { "webworker01_NA", "02dfd5f3cef1142879a7250752feb91ddd722c497fb98c7377c0fcc5ccc201bd55" }, + { "zatjum_SH", "036066fd638b10e555597623e97e032b28b4d1fa5a13c2b0c80c420dbddad236c2" }, + { "titomane_AR", "0268203a4c80047edcd66385c22e764ea5fb8bc42edae389a438156e7dca9a8251" }, + { "chmex_EU", "025b7209ba37df8d9695a23ea706ea2594863ab09055ca6bf485855937f3321d1d" }, + { "indenodes_NA", "02698c6f1c9e43b66e82dbb163e8df0e5a2f62f3a7a882ca387d82f86e0b3fa988" }, + { "patchkez_SH", "02cabd6c5fc0b5476c7a01e9d7b907e9f0a051d7f4f731959955d3f6b18ee9a242" }, + { "metaphilibert_AR", "02adad675fae12b25fdd0f57250b0caf7f795c43f346153a31fe3e72e7db1d6ac6" }, + { "etszombi_EU", "0341adbf238f33a33cc895633db996c3ad01275313ac6641e046a3db0b27f1c880" }, + { "pirate_NA", "02207f27a13625a0b8caef6a7bb9de613ff16e4a5f232da8d7c235c7c5bad72ffe" }, + { "metaphilibert_SH", "0284af1a5ef01503e6316a2ca4abf8423a794e9fc17ac6846f042b6f4adedc3309" }, + { "indenodes_AR", "02ec0fa5a40f47fd4a38ea5c89e375ad0b6ddf4807c99733c9c3dc15fb978ee147" }, + { "chainmakers_NA", "029415a1609c33dfe4a1016877ba35f9265d25d737649f307048efe96e76512877" }, + { "mihailo_EU", "037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941" }, + { "tonyl_AR", "0299684d7291abf90975fa493bf53212cf1456c374aa36f83cc94daece89350ae9" }, + { "alien_NA", "03bea1ac333b95c8669ec091907ea8713cae26f74b9e886e13593400e21c4d30a8" }, + { "pungocloud_SH", "025b97d8c23effaca6fa7efacce20bf54df73081b63004a0fe22f3f98fece5669f" }, + { "node9_EU", "029ffa793b5c3248f8ea3da47fa3cf1810dada5af032ecd0e37bab5b92dd63b34e" }, + { "smdmitry_AR", "022a2a45979a6631a25e4c96469423de720a2f4c849548957c35a35c91041ee7ac" }, + { "nodeone_NA", "03f9dd0484e81174fd50775cb9099691c7d140ff00c0f088847e38dc87da67eb9b" }, + { "gcharang_SH", "02ec4172eab854a0d8cd32bc691c83e93975a3df5a4a453a866736c56e025dc359" }, + { "cipi_EU", "02f2b6defff1c544202f66e47cfd6909c54d67c7c39b9c2a99f137dbaf6d0bd8fa" }, + { "etszombi_AR", "0329944b0ac65b6760787ede042a2fde0be9fca1d80dd756bc0ee0b98d389b7682" }, + { "pbca26_NA", "0387e0fb6f2ca951154c87e16c6cbf93a69862bb165c1a96bcd8722b3af24fe533" }, + { "mylo_SH", "03b58f57822e90fe105e6efb63fd8666033ea503d6cc165b1e479bbd8c2ba033e8" }, + { "swisscertifiers_EU", "03ebcc71b42d88994b8b2134bcde6cb269bd7e71a9dd7616371d9294ec1c1902c5" }, + { "marmarachain_AR", "035bbd81a098172592fe97f50a0ce13cbbf80e55cc7862eccdbd7310fab8a90c4c" }, + { "karasugoi_NA", "0262cf2559703464151153c12e00c4b67a969e39b330301fdcaa6667d7eb02c57d" }, + { "phm87_SH", "021773a38db1bc3ede7f28142f901a161c7b7737875edbb40082a201c55dcf0add" }, + { "oszy_EU", "03d1ffd680491b98a3ec5541715681d1a45293c8efb1722c32392a1d792622596a" }, + { "chmex_AR", "036c856ea778ea105b93c0be187004d4e51161eda32888aa307b8f72d490884005" }, + { "dragonhound_NA", "0227e5cad3731e381df157de189527aac8eb50d82a13ce2bd81153984ebc749515" }, + { "strob_SH", "025ceac4256cef83ca4b110f837a71d70a5a977ecfdf807335e00bc78b560d451a" }, + { "madmax_EU", "02ea0cf4d6d151d0528b07efa79cc7403d77cb9195e2e6c8374f5074b9a787e287" }, + { "dudezmobi_AR", "027ecd974ff2a27a37ee69956cd2e6bb31a608116206f3e31ef186823420182450" }, + { "daemonfox_NA", "022d6f4885f53cbd668ad7d03d4f8e830c233f74e3a918da1ed247edfc71820b3d" }, + { "nutellalicka_SH", "02f4b1e71bc865a79c05fe333952b97cb040d8925d13e83925e170188b3011269b" }, + { "starfleet_EU", "025c7275bd750936862b47793f1f0bb3cbed60fb75a48e7da016e557925fe375eb" }, + { "mrlynch_AR", "031987dc82b087cd53e23df5480e265a5928e9243e0e11849fa12359739d8b18a4" }, + { "greer_NA", "03e0995615d7d3cf1107effa6bdb1133e0876cf1768e923aa533a4e2ee675ec383" }, + { "mcrypt_SH", "025faab3cc2e83bf7dad6a9463cbff86c08800e937942126f258cf219bc2320043" }, + { "decker_EU", "03777777caebce56e17ca3aae4e16374335b156f1dd62ee3c7f8799c6b885f5560" }, + { "dappvader_SH", "02962e2e5af746632016bc7b24d444f7c90141a5f42ce54e361b302cf455d90e6a" }, + { "alright_DEV", "02b73a589d61691efa2ada15c006d27bc18493fea867ce6c14db3d3d28751f8ce3" }, + { "artemii235_DEV", "03bb616b12430bdd0483653de18733597a4fd416623c7065c0e21fe9d96460add1" }, + { "tonyl_DEV", "02d5f7fd6e25d34ab2f3318d60cdb89ff3a812ec5d0212c4c113bb12d12616cfdc" }, + { "decker_DEV", "028eea44a09674dda00d88ffd199a09c9b75ba9782382cc8f1e97c0fd565fe5707" } + } }; #define SETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] |= (1 << ((bitoffset) & 7))) @@ -337,11 +403,11 @@ static const char *notaries_elected[NUM_KMD_SEASONS][NUM_KMD_NOTARIES][2] = #define KOMODO_BIT63SET(x) ((x) & ((uint64_t)1 << 63)) #define KOMODO_VALUETOOBIG(x) ((x) > (uint64_t)10000000001*COIN) -#ifdef TESTMODE_PRICES - #define PRICES_DAYWINDOW (7) -#else - #define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) -#endif +//#ifndef TESTMODE +#define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) +//#else +//#define PRICES_DAYWINDOW (7) +//#endif extern uint8_t ASSETCHAINS_TXPOW,ASSETCHAINS_PUBLIC; extern int8_t ASSETCHAINS_ADAPTIVEPOW; @@ -364,15 +430,15 @@ extern int32_t VERUS_MIN_STAKEAGE; extern uint32_t ASSETCHAINS_VERUSHASH, ASSETCHAINS_VERUSHASHV1_1, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[]; extern std::string NOTARY_PUBKEY,ASSETCHAINS_OVERRIDE_PUBKEY,ASSETCHAINS_SCRIPTPUB; extern uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_MARMARA; -//extern std::vector ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS; +extern std::vector ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS; extern int32_t VERUS_BLOCK_POSUNITS, VERUS_CONSECUTIVE_POS_THRESHOLD, VERUS_NOPOS_THRESHHOLD; extern uint256 KOMODO_EARLYTXID; -extern int32_t KOMODO_CONNECTING,KOMODO_CCACTIVATE,KOMODO_DEALERNODE,KOMODO_DEX_P2P; +extern int32_t KOMODO_CONNECTING,KOMODO_CCACTIVATE,KOMODO_DEALERNODE; extern uint32_t ASSETCHAINS_CC; extern std::string CCerror,ASSETCHAINS_CCLIB; -extern uint8_t ASSETCHAINS_CCDISABLES[256],ASSETCHAINS_CCZEROTXFEE[256]; +extern uint8_t ASSETCHAINS_CCDISABLES[256]; extern int32_t USE_EXTERNAL_PUBKEY; extern std::string NOTARY_PUBKEY,NOTARY_ADDRESS; @@ -417,7 +483,7 @@ int64_t komodo_priceave(int64_t *tmpbuf,int64_t *correlated,int32_t cskip); int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t rawskip,uint32_t *nonzprices,int32_t smoothwidth); int32_t komodo_nextheight(); uint32_t komodo_heightstamp(int32_t height); -int64_t komodo_pricemult_to10e8(int32_t ind); +int64_t komodo_pricemult(int32_t ind); int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblocks); uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight); int32_t komodo_currentheight(); @@ -426,7 +492,6 @@ arith_uint256 komodo_adaptivepow_target(int32_t height,arith_uint256 bnTarget,ui bool komodo_hardfork_active(uint32_t time); int32_t komodo_newStakerActive(int32_t height, uint32_t timestamp); -CBlockIndex *komodo_getblockindex(uint256 hash); uint256 Parseuint256(const char *hexstr); void komodo_sendmessage(int32_t minpeers, int32_t maxpeers, const char *message, std::vector payload); CBlockIndex *komodo_getblockindex(uint256 hash); @@ -434,8 +499,7 @@ int32_t komodo_nextheight(); CBlockIndex *komodo_blockindex(uint256 hash); CBlockIndex *komodo_chainactive(int32_t height); int32_t komodo_blockheight(uint256 hash); -int64_t komodo_get_blocktime(uint256 hash); -bool komodo_txnotarizedconfirmed(uint256 txid,int32_t minconfirms=1); +bool komodo_txnotarizedconfirmed(uint256 txid); int32_t komodo_blockload(CBlock& block, CBlockIndex *pindex); uint32_t komodo_chainactive_timestamp(); uint32_t GetLatestTimestamp(int32_t height); @@ -447,22 +511,4 @@ uint32_t GetLatestTimestamp(int32_t height); #define KOMODO_NSPV_SUPERLITE (KOMODO_NSPV > 0) #endif // !KOMODO_NSPV_SUPERLITE -struct komodo_staking -{ - char address[64]; - uint256 txid; - arith_uint256 hashval; - uint64_t nValue; - uint32_t segid32, txtime; - int32_t vout; - CScript scriptPubKey; -}; -struct komodo_staking *komodo_addutxo(struct komodo_staking *array, int32_t *numkp, int32_t *maxkp, uint32_t txtime, uint64_t nValue, uint256 txid, int32_t vout, char *address, uint8_t *hashbuf, CScript pk); -void komodo_createminerstransactions(); -uint32_t komodo_segid32(char *coinaddr); - -#ifndef _WIN32 -void OS_randombytes(unsigned char *x, long xlen); -#endif - #endif diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index a23e803e2d8..318df7b15b3 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1,4 +1,3 @@ - /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -16,14 +15,11 @@ // paxdeposit equivalent in reverse makes opreturn and KMD does the same in reverse #include "komodo_defs.h" -#include "cc/CCMarmara.h" - -#include "cc/CCPrices.h" -#include "cc/pricesfeed.h" /*#include "secp256k1/include/secp256k1.h" #include "secp256k1/include/secp256k1_schnorrsig.h" #include "secp256k1/include/secp256k1_musig.h" + int32_t dummy_linker_tricker() { secp256k1_context *ctx = 0; std::vector musig64; CPubKey pk; secp256k1_schnorrsig musig; secp256k1_pubkey combined_pk; @@ -31,6 +27,7 @@ int32_t dummy_linker_tricker() return(1); }*/ +int32_t MarmaraValidateCoinbase(int32_t height,CTransaction tx); int32_t pax_fiatstatus(uint64_t *available,uint64_t *deposited,uint64_t *issued,uint64_t *withdrawn,uint64_t *approved,uint64_t *redeemed,char *base) { @@ -745,10 +742,9 @@ int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtim } if ( height > 0 && ASSETCHAINS_MARMARA != 0 && (height & 1) == 0 ) { - std::string errmsg; - if ( MarmaraValidateCoinbase(height,block.vtx[0], errmsg) < 0 ) + if ( MarmaraValidateCoinbase(height,block.vtx[0]) < 0 ) { - fprintf(stderr,"MARMARA validate coinbase error ht.%d %s\n",height, errmsg.c_str()); + fprintf(stderr,"MARMARA error ht.%d constrains even height blocks to pay 100%% to CC in vout0 with opreturn\n",height); return(-1); } } @@ -1257,6 +1253,7 @@ void komodo_stateind_set(struct komodo_state *sp,uint32_t *inds,int32_t n,uint8_ printf("numR.%d numV.%d numN.%d count.%d\n",numR,numV,numN,count); /*else if ( func == 'K' ) // KMD height: stop after 1st else if ( func == 'T' ) // KMD height+timestamp: stop after 1st + else if ( func == 'N' ) // notarization, scan backwards 1440+ blocks; else if ( func == 'V' ) // price feed: can stop after 1440+ else if ( func == 'R' ) // opreturn:*/ @@ -1276,7 +1273,7 @@ void *OS_loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) { fclose(fp); *lenp = 0; - //printf("OS_loadfile null size.(%s)\n",fname); + printf("OS_loadfile null size.(%s)\n",fname); return(0); } if ( filesize > buflen ) @@ -1601,13 +1598,11 @@ extern std::vector Mineropret; // opreturn data set by the data gatheri #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) -/* const char *Cryptos[] = { "KMD", "ETH" }; // must be on binance (for now) // "LTC", "BCHABC", "XMR", "IOTA", "ZEC", "WAVES", "LSK", "DCR", "RVN", "DASH", "XEM", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" const char *Forex[] = { "BGN","NZD","ILS","RUB","CAD","PHP","CHF","AUD","JPY","TRY","HKD","MYR","HRK","CZK","IDR","DKK","NOK","HUF","GBP","MXN","THB","ISK","ZAR","BRL","SGD","PLN","INR","KRW","RON","CNY","SEK","EUR" }; // must be in ECB list -*/ struct komodo_extremeprice { @@ -1620,7 +1615,7 @@ struct komodo_extremeprice struct komodo_priceinfo { FILE *fp; - char symbol[PRICES_MAXNAMELENGTH]; // TODO: it was 64 + char symbol[64]; } PRICES[KOMODO_MAXPRICES]; uint32_t PriceCache[KOMODO_LOCALPRICE_CACHESIZE][KOMODO_MAXPRICES];//4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex)]; @@ -1706,14 +1701,14 @@ uint32_t komodo_pricenew(char *maxflagp,uint32_t price,uint32_t refprice,int64_t } // komodo_pricecmp() returns -1 if any of the prices are beyond the tolerance -int32_t komodo_pricecmp(int32_t nHeight, char *maxflags, uint32_t *pricebitsA, uint32_t *pricebitsB, int32_t begin, int32_t end, int64_t tolerance) +int32_t komodo_pricecmp(int32_t nHeight,int32_t n,char *maxflags,uint32_t *pricebitsA,uint32_t *pricebitsB,int64_t tolerance) { int32_t i; uint32_t newprice; - for (i = begin; i < end; i++) + for (i=1; i newprice.%u out of tolerance maxflag.%d\n", nHeight, i, end, pricebitsB[i], pricebitsA[i], newprice, maxflags[i]); + fprintf(stderr,"ht.%d i.%d/%d %u vs %u -> newprice.%u out of tolerance maxflag.%d\n",nHeight,i,n,pricebitsB[i],pricebitsA[i],newprice,maxflags[i]); return(-1); } } @@ -1721,18 +1716,15 @@ int32_t komodo_pricecmp(int32_t nHeight, char *maxflags, uint32_t *pricebitsA, u } // komodo_priceclamp() clamps any price that is beyond tolerance -int32_t komodo_priceclamp(uint32_t *pricebits, uint32_t *refprices, int32_t begin, int32_t end, int64_t tolerance) +int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int64_t tolerance) { - int32_t i; - uint32_t newprice; - char maxflags[KOMODO_MAXPRICES]; - - memset(maxflags, 0, sizeof(maxflags)); - for (i = begin; i < end; i++) + int32_t i; uint32_t newprice; char maxflags[KOMODO_MAXPRICES]; + memset(maxflags,0,sizeof(maxflags)); + for (i=1; i %u\n", i, end, refprices[i], pricebits[i], newprice); + fprintf(stderr,"priceclamped[%d of %d] %u vs %u -> %u\n",i,n,refprices[i],pricebits[i],newprice); pricebits[i] = newprice; } } @@ -1742,76 +1734,56 @@ int32_t komodo_priceclamp(uint32_t *pricebits, uint32_t *refprices, int32_t begi // komodo_mineropret() returns a valid pricedata to add to the coinbase opreturn for nHeight CScript komodo_mineropret(int32_t nHeight) { - CScript opret; - char maxflags[KOMODO_MAXPRICES]; - uint32_t pricebits[KOMODO_MAXPRICES], prevbits[KOMODO_MAXPRICES]; - int32_t maxflag, i, n, numzero = 0; - - if (Mineropret.size() >= PRICES_SIZEBIT0) + CScript opret; char maxflags[KOMODO_MAXPRICES]; uint32_t pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES]; int32_t maxflag,i,n,numzero=0; + if ( Mineropret.size() >= PRICES_SIZEBIT0 ) { n = (int32_t)(Mineropret.size() / sizeof(uint32_t)); numzero = 1; - while (numzero > 0) + while ( numzero > 0 ) { - memcpy(pricebits, Mineropret.data(), Mineropret.size()); - for (i = numzero = 0; i < n; i++) - { - if (pricebits[i] == 0) + memcpy(pricebits,Mineropret.data(),Mineropret.size()); + for (i=numzero=0; i processors; - PricesFeedGetCustomProcessors(processors); - - if (komodo_heightpricebits(0, prevbits, nHeight - 1) > 0) // get previous prices + if ( komodo_heightpricebits(0,prevbits,nHeight-1) > 0 ) { - // set working prices buffer: - memcpy(pricebits, Mineropret.data(), Mineropret.size()); - memset(maxflags, 0, sizeof(maxflags)); - - for (auto const &p : processors) + memcpy(pricebits,Mineropret.data(),Mineropret.size()); + memset(maxflags,0,sizeof(maxflags)); + if ( komodo_pricecmp(0,n,maxflags,pricebits,prevbits,PRICES_ERRORRATE) < 0 ) { - if (p.clamper == NULL) - { - // default clamper - if (komodo_pricecmp(nHeight, maxflags, pricebits, prevbits, p.b, p.e, PRICES_ERRORRATE) < 0) - { - // if the new prices are outside tolerance, update Mineropret with clamped prices - komodo_priceclamp(pricebits, prevbits, p.b, p.e, PRICES_ERRORRATE); - //fprintf(stderr,"update Mineropret to clamped prices\n"); - - } - } - else - p.clamper(nHeight, pricebits, prevbits, p.b, p.e, PRICES_ERRORRATE); + // if the new prices are outside tolerance, update Mineropret with clamped prices + komodo_priceclamp(n,pricebits,prevbits,PRICES_ERRORRATE); + //fprintf(stderr,"update Mineropret to clamped prices\n"); + memcpy(Mineropret.data(),pricebits,Mineropret.size()); } - - // copy working buffer back: - memcpy(Mineropret.data(), pricebits, Mineropret.size()); } - int32_t i; - for (i = 0; i < Mineropret.size(); i++) - fprintf(stderr, "%02x", Mineropret[i]); - fprintf(stderr, " <- Mineropret\n"); + for (i=0; i vopret; - char maxflags[KOMODO_MAXPRICES]; - uint256 bhash; double btcusd, btcgbp, btceur; - uint32_t localbits[KOMODO_MAXPRICES], pricebits[KOMODO_MAXPRICES], prevbits[KOMODO_MAXPRICES], newprice; - int32_t prevtime, lag, lag2, lag3, n, errflag, iter; - uint32_t now; - const int PRICES_EARLY_CHAIN_HEIGHT = 1000; - - - if (ASSETCHAINS_CBOPRET != 0 && nHeight > 0) + std::vector vopret; char maxflags[KOMODO_MAXPRICES]; uint256 bhash; double btcusd,btcgbp,btceur; uint32_t localbits[KOMODO_MAXPRICES],pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now; + now = (uint32_t)time(NULL); + if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { - now = (uint32_t)time(NULL); bhash = block->GetHash(); - GetOpReturnData(scriptPubKey, vopret); - if (vopret.size() >= PRICES_SIZEBIT0) + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() >= PRICES_SIZEBIT0 ) { - // default prices validation: n = (int32_t)(vopret.size() / sizeof(uint32_t)); - memcpy(pricebits, vopret.data(), Mineropret.size()); - - if (nHeight > 2) + memcpy(pricebits,vopret.data(),Mineropret.size()); + memset(maxflags,0,sizeof(maxflags)); + if ( nHeight > 2 ) { - if (komodo_heightpricebits(0, prevbits, nHeight - 1) <= 0) - { - std::cerr << __func__ << " error: could not get prevbits for height " << nHeight-1 << std::endl; - return -1; - } - - std::vector processors; - PricesFeedGetCustomProcessors(processors); - - std::vector< std::pair > ranges; - for (const auto &p : processors) - { - if (p.validator) - { - // call the custom validator - if (p.validator(nHeight, ASSETCHAINS_BLOCKTIME, block->nTime, previndex->nTime, pricebits, prevbits, p.b, p.e) < 0) - std::cerr << __func__ << " error: custom validation function invalidated prices data for prices indexes from " << p.b << " to " << p.e << std::endl; - } - else - { - // create ranges for the default validation: - ranges.push_back(std::make_pair(p.b, p.e)); - } - } - - // proceed with the default validation: - memset(maxflags, 0, sizeof(maxflags)); prevtime = previndex->nTime; lag = (int32_t)(now - pricebits[0]); lag2 = (int32_t)(pricebits[0] - prevtime); lag3 = (int32_t)(block->nTime - pricebits[0]); - - if (lag < -60) // avoid data from future + if ( lag < -60 ) // avoid data from future { - fprintf(stderr, "A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n", nHeight, now, prevtime, block->nTime, pricebits[0], lag, lag2, lag3); + fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } - // we should allow lag2 check to pass for early chains - // currently minimum prices update interval = 120 sec (really 120..130) - // suppose blocktime = 180 sec - // block[i], t = T0 - // block[i+1], t = T0+180 - // earliest update t <= T0+180-130 <= T0+50 - // so lag2 could be not less than +50 on mature chain with blocktime = 180 - // but on the initial chain stage blocks could be generated faster, in several secs (> 10 sec) - // block[i], t = T0 - // block[i+1], t = T0+10 - // earliest Tupdate <= T0+10-130 <= T0-120 - // as lag2 could be even -120 for blocks per 10 sec let's allow lag2 to be not less than -130 for early chains - if (lag2 < -60 && nHeight >= PRICES_EARLY_CHAIN_HEIGHT || lag2 < -130) //testchain_exemption ) // must be close to last block timestamp + if ( lag2 < -60 ) //testchain_exemption ) // must be close to last block timestamp { - fprintf(stderr, "B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n", nHeight, now, prevtime, block->nTime, pricebits[0], lag, lag2, lag3, ASSETCHAINS_BLOCKTIME, lag2 < -ASSETCHAINS_BLOCKTIME); - if (nHeight > testchain_exemption) + fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); + if ( nHeight > testchain_exemption ) return(-1); } - - // explanation for lag3: - // lag3 < -60 check violation could be possible if a validation node has clock sync problems - // lag3 > ASSETCHAINS_BLOCKTIME could be possible if update interval is more than blocktime: - // block[i] t = T0 - // block[i+1] t = T0+180 - // earliest update t = T0+180-130 = T0+50 - // lag3 = 130, this is less than min blocktime = 180 - // (on early chains this also will work as we check ASSETCHAINS_BLOCKTIME param value and not real early block generation time) - if (lag3 < -60 || lag3 > ASSETCHAINS_BLOCKTIME) + if ( lag3 < -60 || lag3 > ASSETCHAINS_BLOCKTIME ) { - fprintf(stderr, "C ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n", nHeight, now, prevtime, block->nTime, pricebits[0], lag, lag2, lag3); - if (nHeight > testchain_exemption) + fprintf(stderr,"C ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); + if ( nHeight > testchain_exemption ) return(-1); } - - // basic price values (always present in config): - btcusd = (double)pricebits[1] / 10000; - btcgbp = (double)pricebits[2] / 10000; - btceur = (double)pricebits[3] / 10000; - fprintf(stderr, "ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n", nHeight, lag, btcusd, btcgbp, btceur, btcusd / btcgbp, btcusd / btceur, btcgbp / btceur, lag2); - - if (nHeight < testchain_exemption) + btcusd = (double)pricebits[1]/10000; + btcgbp = (double)pricebits[2]/10000; + btceur = (double)pricebits[3]/10000; + fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); + if ( komodo_heightpricebits(0,prevbits,nHeight-1) > 0 ) { - // set for a test chain - for (auto const &r : ranges) { - for (int32_t i = r.first; i < r.second; i++) - if (pricebits[i] == 0) + if ( nHeight < testchain_exemption ) + { + for (i=0; i= PRICES_SIZEBIT0) + } else return(-1); + if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) { - memcpy(localbits, Mineropret.data(), Mineropret.size()); - if (nHeight < testchain_exemption) + memcpy(localbits,Mineropret.data(),Mineropret.size()); + if ( nHeight < testchain_exemption ) { - // set for a test chain - for (auto const &r : ranges) { - for (int32_t i = r.first; i < r.second; i++) - if (localbits[i] == 0) - localbits[i] = prevbits[i]; - } - localbits[0] = prevbits[0]; // store timestamp + for (i=0; i 0 && localbits[i] < prevbits[i] ) { - // make sure local price is moving in right direction - fprintf(stderr, "maxflag.%d i.%d localbits.%u vs pricebits.%u prevbits.%u\n", maxflag, i, localbits[i], pricebits[i], prevbits[i]); - if (maxflag > 0 && localbits[i] < prevbits[i]) - { - if (iter == 0) { - checked = true; - break; - } - // second iteration checks recent prices to see if within local volatility - int32_t icache; - for (icache = 0; icache < KOMODO_LOCALPRICE_CACHESIZE; icache++) - { - if (PriceCache[icache][i] >= prevbits[i]) - { - fprintf(stderr, "i.%d within recent localprices[%d] %u >= %u\n", i, icache, PriceCache[icache][i], prevbits[i]); - checked = true; - break; - } - } - if (icache == KOMODO_LOCALPRICE_CACHESIZE) + if ( iter == 0 ) + break; + // second iteration checks recent prices to see if within local volatility + for (j=0; j= prevbits[i] ) { - komodo_queuelocalprice(1, nHeight, block->nTime, bhash, i, prevbits[i]); - checked = true; + fprintf(stderr,"i.%d within recent localprices[%d] %u >= %u\n",i,j,PriceCache[j][i],prevbits[i]); break; } - } - else if (maxflag < 0 && localbits[i] > prevbits[i]) + if ( j == KOMODO_LOCALPRICE_CACHESIZE ) { - if (iter == 0) { - checked = true; - break; - } - int32_t icache; - for (icache = 0; icache < KOMODO_LOCALPRICE_CACHESIZE; icache++) - { - if (PriceCache[icache][i] <= prevbits[i]) - { - fprintf(stderr, "i.%d within recent localprices[%d] %u <= prev %u\n", i, icache, PriceCache[icache][i], prevbits[i]); - checked = true; - break; - } - } - if (icache == KOMODO_LOCALPRICE_CACHESIZE) + komodo_queuelocalprice(1,nHeight,block->nTime,bhash,i,prevbits[i]); + break; + } + } + else if ( maxflag < 0 && localbits[i] > prevbits[i] ) + { + if ( iter == 0 ) + break; + for (j=0; jnTime, bhash, i, prevbits[i]); - checked = true; + fprintf(stderr,"i.%d within recent localprices[%d] %u <= prev %u\n",i,j,PriceCache[j][i],prevbits[i]); break; } + if ( j == KOMODO_LOCALPRICE_CACHESIZE ) + { + komodo_queuelocalprice(-1,nHeight,block->nTime,bhash,i,prevbits[i]); + break; } } } - if (checked) - break; // break from ranges } - if (checked) + if ( i != n ) { - if (iter == 0) + if ( iter == 0 ) { - fprintf(stderr, "force update prices\n"); + fprintf(stderr,"force update prices\n"); komodo_cbopretupdate(1); - memcpy(localbits, Mineropret.data(), Mineropret.size()); - } - else - { - std::cerr << __func__ << " prices opreturn not valid" << std::endl; - return(-1); - } + memcpy(localbits,Mineropret.data(),Mineropret.size()); + } else return(-1); } } } } - if (bhash == ExtremePrice.blockhash) + if ( bhash == ExtremePrice.blockhash ) { - fprintf(stderr, "approved a previously extreme price based on new data ht.%d vs %u vs %u\n", ExtremePrice.height, ExtremePrice.timestamp, (uint32_t)block->nTime); - memset(&ExtremePrice, 0, sizeof(ExtremePrice)); + fprintf(stderr,"approved a previously extreme price based on new data ht.%d vs %u vs %u\n",ExtremePrice.height,ExtremePrice.timestamp,(uint32_t)block->nTime); + memset(&ExtremePrice,0,sizeof(ExtremePrice)); } return(0); - } - else - fprintf(stderr, "wrong (too big) prices opreturn size %d vs %d, scriptPubKey size %d [%02x]\n", (int32_t)vopret.size(), (int32_t)Mineropret.size(), (int32_t)scriptPubKey.size(), (scriptPubKey.size() > 0 ? scriptPubKey[0] : '\0')); + } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)Mineropret.size(),(int32_t)scriptPubKey.size(),scriptPubKey[0]); return(-1); } return(0); @@ -2175,32 +2037,210 @@ const char *Techstocks[] = { "AAPL","ADBE","ADSK","AKAM","AMD","AMZN","ATVI","BB","CDW","CRM","CSCO","CYBR","DBX","EA","FB","GDDY","GOOG","GRMN","GSAT","HPQ","IBM","INFY","INTC","INTU","JNPR","MSFT","MSI","MU","MXL","NATI","NCR","NFLX","NTAP","NVDA","ORCL","PANW","PYPL","QCOM","RHT","S","SHOP","SNAP","SPOT","SYMC","SYNA","T","TRIP","TWTR","TXN","VMW","VOD","VRSN","VZ","WDC","XRX","YELP","YNDX","ZEN" }; const char *Metals[] = { "XAU", "XAG", "XPT", "XPD", }; + const char *Markets[] = { "DJIA", "SPX", "NDX", "VIX" }; */ cJSON *get_urljson(char *url) { - char *jsonstr; - cJSON *json = 0; + char *jsonstr; cJSON *json = 0; if ( (jsonstr= issue_curl(url)) != 0 ) { //fprintf(stderr,"(%s) -> (%s)\n",url,jsonstr); json = cJSON_Parse(jsonstr); - //free(jsonstr); - cJSON_free(jsonstr); + free(jsonstr); } return(json); } +int32_t get_stockprices(uint32_t now,uint32_t *prices,std::vector symbols) +{ + char url[32768],*symbol,*timestr; cJSON *json,*obj; int32_t i,n=0,retval=-1; uint32_t uprice,timestamp; + sprintf(url,"https://api.iextrading.com/1.0/tops/last?symbols=%s",GetArg("-ac_stocks","").c_str()); + fprintf(stderr,"url.(%s)\n",url); + if ( (json= get_urljson(url)) != 0 ) //if ( (json= send_curl(url,(char *)"iex")) != 0 ) // + { + fprintf(stderr,"stocks.(%s)\n",jprint(json,0)); + if ( (n= cJSON_GetArraySize(json)) > 0 ) + { + retval = n; + for (i=0; i now+60 || timestamp < now-ASSETCHAINS_BLOCKTIME ) + { + fprintf(stderr,"time error.%d (%u vs %u)\n",timestamp-now,timestamp,now); + retval = -1; + }*/ + if ( symbols[i] != symbol ) + { + retval = -1; + fprintf(stderr,"MISMATCH."); + } + fprintf(stderr,"(%s %u) ",symbol,uprice); + } + } + fprintf(stderr,"numstocks.%d\n",n); + } + //https://api.iextrading.com/1.0/tops/last?symbols=AAPL -> [{"symbol":"AAPL","price":198.63,"size":100,"time":1555092606076}] + free_json(json); + } + return(retval); +} + +uint32_t get_dailyfx(uint32_t *prices) +{ + //{"base":"USD","rates":{"BGN":1.74344803,"NZD":1.471652701,"ILS":3.6329113924,"RUB":65.1997682296,"CAD":1.3430201462,"USD":1.0,"PHP":52.8641469068,"CHF":0.9970582992,"AUD":1.4129078267,"JPY":110.6792654662,"TRY":5.6523444464,"HKD":7.8499732573,"MYR":4.0824567659,"HRK":6.6232840078,"CZK":22.9862720628,"IDR":14267.4986628633,"DKK":6.6551078624,"NOK":8.6806917454,"HUF":285.131039401,"GBP":0.7626582278,"MXN":19.4183455161,"THB":31.8702085933,"ISK":122.5708682475,"ZAR":14.7033339276,"BRL":3.9750401141,"SGD":1.3573720806,"PLN":3.8286682118,"INR":69.33187734,"KRW":1139.1602781244,"RON":4.2423783206,"CNY":6.7387234801,"SEK":9.3385630237,"EUR":0.8914244963},"date":"2019-03-28"} + char url[512],*datestr; cJSON *json,*rates; int32_t i; uint32_t datenum=0,price = 0; + sprintf(url,"https://api.openrates.io/latest?base=USD"); + if ( (json= get_urljson(url)) != 0 ) //if ( (json= send_curl(url,(char *)"dailyfx")) != 0 ) + { + if ( (rates= jobj(json,(char *)"rates")) != 0 ) + { + for (i=0; i strvec) +{ + int32_t i,errs=0; uint32_t price; char *symbol; + for (i=0; i 333 ) // for debug only! +// ASSETCHAINS_CBOPRET = 7; + size = komodo_cbopretsize(ASSETCHAINS_CBOPRET); + if ( Mineropret.size() < size ) + Mineropret.resize(size); + size = PRICES_SIZEBIT0; + if ( (forceflag != 0 || now > lastbtc+120) && get_btcusd(pricebits) == 0 ) + { + if ( flags == 0 ) + komodo_PriceCache_shift(); + memcpy(PriceCache[0],pricebits,PRICES_SIZEBIT0); + flags |= 1; } - - if (count > 0) { - komodo_PriceCache_shift(); - memcpy(PriceCache[0], pricebuf, count * sizeof(uint32_t)); - flags = 1; //old code compatibility + if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) + { + if ( now > lasttime+3600*5 || forexprices[0] == 0 ) // cant assume timestamp is valid for forex price as it is a daily weekday changing thing anyway. + { + get_dailyfx(forexprices); + if ( flags == 0 ) + komodo_PriceCache_shift(); + flags |= 2; + memcpy(&PriceCache[0][size/sizeof(uint32_t)],forexprices,sizeof(forexprices)); + } + size += (sizeof(Forex)/sizeof(*Forex)) * sizeof(uint32_t); + } + if ( (ASSETCHAINS_CBOPRET & 4) != 0 ) + { + if ( forceflag != 0 || flags != 0 ) + { + get_cryptoprices(pricebuf,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos)),ASSETCHAINS_PRICES); + if ( flags == 0 ) + komodo_PriceCache_shift(); + memcpy(&PriceCache[0][size/sizeof(uint32_t)],pricebuf,(sizeof(Cryptos)/sizeof(*Cryptos)+ASSETCHAINS_PRICES.size()) * sizeof(uint32_t)); + flags |= 4; // very rarely we can see flags == 6 case + } + size += (sizeof(Cryptos)/sizeof(*Cryptos)+ASSETCHAINS_PRICES.size()) * sizeof(uint32_t); + } + now = (uint32_t)time(NULL); + if ( (ASSETCHAINS_CBOPRET & 8) != 0 ) + { + if ( forceflag != 0 || flags != 0 ) + { + if ( get_stockprices(now,pricebuf,ASSETCHAINS_STOCKS) == ASSETCHAINS_STOCKS.size() ) + { + if ( flags == 0 ) + komodo_PriceCache_shift(); + memcpy(&PriceCache[0][size/sizeof(uint32_t)],pricebuf,ASSETCHAINS_STOCKS.size() * sizeof(uint32_t)); + flags |= 8; // very rarely we can see flags == 10 case + } + } + size += (ASSETCHAINS_STOCKS.size()) * sizeof(uint32_t); } - if ( flags != 0 ) { - uint32_t opretsize = komodo_cbopretsize(ASSETCHAINS_CBOPRET); - if (Mineropret.size() < opretsize) - Mineropret.resize(opretsize); - -// if ( (flags & 1) != 0 ) -// lastbtc = now; -// if ( (flags & 2) != 0 ) -// lasttime = now; - memcpy(Mineropret.data(), PriceCache[0], opretsize); - - // invalidate block with extreme prices: - if ( ExtremePrice.dir != 0 && ExtremePrice.ind > 0 && ExtremePrice.ind < count && now < ExtremePrice.timestamp+3600 ) + if ( (flags & 1) != 0 ) + lastbtc = now; + if ( (flags & 2) != 0 ) + lasttime = now; + memcpy(Mineropret.data(),PriceCache[0],size); + if ( ExtremePrice.dir != 0 && ExtremePrice.ind > 0 && ExtremePrice.ind < size/sizeof(uint32_t) && now < ExtremePrice.timestamp+3600 ) { fprintf(stderr,"cmp dir.%d PriceCache[0][ExtremePrice.ind] %u >= %u ExtremePrice.pricebits\n",ExtremePrice.dir,PriceCache[0][ExtremePrice.ind],ExtremePrice.pricebits); if ( (ExtremePrice.dir > 0 && PriceCache[0][ExtremePrice.ind] >= ExtremePrice.pricebits) || (ExtremePrice.dir < 0 && PriceCache[0][ExtremePrice.ind] <= ExtremePrice.pricebits) ) @@ -2280,20 +2346,35 @@ void komodo_cbopretupdate(int32_t forceflag) //fprintf(stderr," <- set Mineropret[%d] size.%d %ld\n",(int32_t)Mineropret.size(),size,sizeof(PriceCache[0])); } } - pending = 0; // allow entry to the function + pending = 0; } -// get multiplier to normalize prices to 100,000,000 decimal order, to make synthetic indexes -int64_t komodo_pricemult_to10e8(int32_t ind) +int64_t komodo_pricemult(int32_t ind) { int32_t i,j; if ( (ASSETCHAINS_CBOPRET & 1) != 0 && ind < KOMODO_MAXPRICES ) { - int64_t conversionmult = PricesFeedMultiplier(ind); - if (conversionmult >= 1) - return COIN / conversionmult; - else - return COIN; // this should not happen + if ( PriceMult[0] == 0 ) + { + for (i=0; i<4; i++) + PriceMult[i] = 10000; + if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) + { + for (j=0; j= KOMODO_MAXPRICES ) return(-1); - mult = komodo_pricemult_to10e8(ind); + mult = komodo_pricemult(ind); if ( nonzprices != 0 ) memset(nonzprices,0,sizeof(*nonzprices)*PRICES_DAYWINDOW); //for (i=0; i maxcorrelation ) maxcorrelation = correlation; } - //fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d ref.%llu high.%llu low.%llu\n",ind,iter,maxcorrelation,(long long)refprice,(long long)highprice,(long long)lowprice); + fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d ref.%llu high.%llu low.%llu\n",ind,iter,maxcorrelation,(long long)refprice,(long long)highprice,(long long)lowprice); return(0); } @@ -2691,11 +2820,11 @@ void komodo_pricesupdate(int32_t height,CBlock *pblock) } else fprintf(stderr,"error price_smoothed ht.%d ind.%d\n",height,ind); } else fprintf(stderr,"error fread ptr64 for ht.%d ind.%d\n",height,ind); } - } //else fprintf(stderr,"error komodo_pricecorrelated for ht.%d ind.%d\n",height,ind); + } else fprintf(stderr,"error komodo_pricecorrelated for ht.%d ind.%d\n",height,ind); } - //fprintf(stderr,"height.%d\n",height); + fprintf(stderr,"height.%d\n",height); } else fprintf(stderr,"error reading rawprices for ht.%d\n",height); - } // else fprintf(stderr,"height.%d <= width.%d\n",height,width); + } else fprintf(stderr,"height.%d <= width.%d\n",height,width); pthread_mutex_unlock(&pricemutex); } else fprintf(stderr,"null PRICES[0].fp\n"); } else fprintf(stderr,"numprices mismatch, height.%d\n",height); @@ -2714,42 +2843,3 @@ int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblo pthread_mutex_unlock(&pricemutex); return(retval); } - -// place to add miner's created transactions -UniValue sendrawtransaction(const UniValue& params, bool fHelp, const CPubKey &mypk); - -void komodo_createminerstransactions() -{ - std::vector minersTransactions; - CBlockIndex *pIndexTip = chainActive.LastTip(); - int32_t nHeight = pIndexTip ? pIndexTip->GetHeight() : 0; - - if(ASSETCHAINS_MARMARA != 0) - { - MarmaraRunAutoSettlement(nHeight, minersTransactions); // run Marmara autosettlement, returns settlement transactions - } - // TODO create 'kogs' transactions... - - // send miner created transaction - CPubKey minerpk = pubkey2pk(Mypubkey()); - for (const auto &tx : minersTransactions) - { - std::string hextx = HexStr(E_MARSHAL(ss << tx)); - UniValue rpcparams(UniValue::VARR), txparam(UniValue::VOBJ); - txparam.setStr(hextx); - rpcparams.push_back(txparam); - try { - // TODO: change sendrawtransaction to low-level RelayTransaction function - sendrawtransaction(rpcparams, false, CPubKey()); // NOTE: throws error, so catch them! - } - catch (std::runtime_error error) - { - LOGSTREAMFN("miner", CCLOG_ERROR, stream << std::string("could not send miner created transaction: bad parameters: ") + error.what()); - } - catch (UniValue error) - { - LOGSTREAMFN("miner", CCLOG_ERROR, stream << std::string("error: could not send miner created tx: ") + error.getValStr()); - } - } - -} diff --git a/src/komodo_gateway.old b/src/komodo_gateway.old deleted file mode 100644 index ab2fdd6e8f0..00000000000 --- a/src/komodo_gateway.old +++ /dev/null @@ -1,2845 +0,0 @@ -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -// paxdeposit equivalent in reverse makes opreturn and KMD does the same in reverse -#include "komodo_defs.h" - -/*#include "secp256k1/include/secp256k1.h" -#include "secp256k1/include/secp256k1_schnorrsig.h" -#include "secp256k1/include/secp256k1_musig.h" - -int32_t dummy_linker_tricker() -{ - secp256k1_context *ctx = 0; std::vector musig64; CPubKey pk; secp256k1_schnorrsig musig; secp256k1_pubkey combined_pk; - if ( secp256k1_schnorrsig_parse((const secp256k1_context *)ctx,&musig,(const uint8_t *)&musig64[0]) > 0 && secp256k1_ec_pubkey_parse(ctx,&combined_pk,pk.begin(),33) > 0 ) - return(1); -}*/ - -int32_t MarmaraValidateCoinbase(int32_t height,CTransaction tx); - -int32_t pax_fiatstatus(uint64_t *available,uint64_t *deposited,uint64_t *issued,uint64_t *withdrawn,uint64_t *approved,uint64_t *redeemed,char *base) -{ - int32_t baseid; struct komodo_state *sp; int64_t netliability,maxallowed,maxval; - *available = *deposited = *issued = *withdrawn = *approved = *redeemed = 0; - if ( (baseid= komodo_baseid(base)) >= 0 ) - { - if ( (sp= komodo_stateptrget(base)) != 0 ) - { - *deposited = sp->deposited; - *issued = sp->issued; - *withdrawn = sp->withdrawn; - *approved = sp->approved; - *redeemed = sp->redeemed; - maxval = sp->approved; - if ( sp->withdrawn > maxval ) - maxval = sp->withdrawn; - netliability = (sp->issued - maxval) - sp->shorted; - maxallowed = komodo_maxallowed(baseid); - if ( netliability < maxallowed ) - *available = (maxallowed - netliability); - //printf("%llu - %llu %s %.8f %.8f %.8f %.8f %.8f\n",(long long)maxallowed,(long long)netliability,base,dstr(*deposited),dstr(*issued),dstr(*withdrawn),dstr(*approved),dstr(*redeemed)); - return(0); - } else printf("pax_fiatstatus cant get basesp.%s\n",base); - } // else printf("pax_fiatstatus illegal base.%s\n",base); - return(-1); -} - -void pax_keyset(uint8_t *buf,uint256 txid,uint16_t vout,uint8_t type) -{ - memcpy(buf,&txid,32); - memcpy(&buf[32],&vout,2); - buf[34] = type; -} - -struct pax_transaction *komodo_paxfind(uint256 txid,uint16_t vout,uint8_t type) -{ - struct pax_transaction *pax; uint8_t buf[35]; - pthread_mutex_lock(&komodo_mutex); - pax_keyset(buf,txid,vout,type); - HASH_FIND(hh,PAX,buf,sizeof(buf),pax); - pthread_mutex_unlock(&komodo_mutex); - return(pax); -} - -struct pax_transaction *komodo_paxfinds(uint256 txid,uint16_t vout) -{ - struct pax_transaction *pax; int32_t i; uint8_t types[] = { 'I', 'D', 'X', 'A', 'W' }; - for (i=0; itxid = txid; - pax->vout = vout; - pax->type = type; - memcpy(pax->buf,buf,sizeof(pax->buf)); - HASH_ADD_KEYPTR(hh,PAX,pax->buf,sizeof(pax->buf),pax); - //printf("ht.%d create pax.%p mark.%d\n",height,pax,mark); - } - if ( pax != 0 ) - { - pax->marked = mark; - //if ( height > 214700 || pax->height > 214700 ) - // printf("mark ht.%d %.8f %.8f\n",pax->height,dstr(pax->komodoshis),dstr(pax->fiatoshis)); - - } - pthread_mutex_unlock(&komodo_mutex); - return(pax); -} - -void komodo_paxdelete(struct pax_transaction *pax) -{ - return; // breaks when out of order - pthread_mutex_lock(&komodo_mutex); - HASH_DELETE(hh,PAX,pax); - pthread_mutex_unlock(&komodo_mutex); -} - -void komodo_gateway_deposit(char *coinaddr,uint64_t value,char *symbol,uint64_t fiatoshis,uint8_t *rmd160,uint256 txid,uint16_t vout,uint8_t type,int32_t height,int32_t otherheight,char *source,int32_t approved) // assetchain context -{ - struct pax_transaction *pax; uint8_t buf[35]; int32_t addflag = 0; struct komodo_state *sp; char str[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN],*s; - //if ( KOMODO_PAX == 0 ) - // return; - //if ( strcmp(symbol,ASSETCHAINS_SYMBOL) != 0 ) - // return; - sp = komodo_stateptr(str,dest); - pthread_mutex_lock(&komodo_mutex); - pax_keyset(buf,txid,vout,type); - HASH_FIND(hh,PAX,buf,sizeof(buf),pax); - if ( pax == 0 ) - { - pax = (struct pax_transaction *)calloc(1,sizeof(*pax)); - pax->txid = txid; - pax->vout = vout; - pax->type = type; - memcpy(pax->buf,buf,sizeof(pax->buf)); - HASH_ADD_KEYPTR(hh,PAX,pax->buf,sizeof(pax->buf),pax); - addflag = 1; - if ( 0 && ASSETCHAINS_SYMBOL[0] == 0 ) - { - int32_t i; for (i=0; i<32; i++) - printf("%02x",((uint8_t *)&txid)[i]); - printf(" v.%d [%s] kht.%d ht.%d create pax.%p symbol.%s source.%s\n",vout,ASSETCHAINS_SYMBOL,height,otherheight,pax,symbol,source); - } - } - pthread_mutex_unlock(&komodo_mutex); - if ( coinaddr != 0 ) - { - strcpy(pax->coinaddr,coinaddr); - if ( value != 0 ) - pax->komodoshis = value; - if ( symbol != 0 ) - strcpy(pax->symbol,symbol); - if ( source != 0 ) - strcpy(pax->source,source); - if ( fiatoshis != 0 ) - pax->fiatoshis = fiatoshis; - if ( rmd160 != 0 ) - memcpy(pax->rmd160,rmd160,20); - if ( height != 0 ) - pax->height = height; - if ( otherheight != 0 ) - pax->otherheight = otherheight; - } - else - { - pax->marked = height; - //printf("pax.%p MARK DEPOSIT ht.%d other.%d\n",pax,height,otherheight); - } -} - -int32_t komodo_rwapproval(int32_t rwflag,uint8_t *opretbuf,struct pax_transaction *pax) -{ - int32_t i,len = 0; - if ( rwflag == 1 ) - { - for (i=0; i<32; i++) - opretbuf[len++] = ((uint8_t *)&pax->txid)[i]; - opretbuf[len++] = pax->vout & 0xff; - opretbuf[len++] = (pax->vout >> 8) & 0xff; - } - else - { - for (i=0; i<32; i++) - ((uint8_t *)&pax->txid)[i] = opretbuf[len++]; - //for (i=0; i<32; i++) - // printf("%02x",((uint8_t *)&pax->txid)[31-i]); - pax->vout = opretbuf[len++]; - pax->vout += ((uint32_t)opretbuf[len++] << 8); - //printf(" txid v.%d\n",pax->vout); - } - len += iguana_rwnum(rwflag,&opretbuf[len],sizeof(pax->komodoshis),&pax->komodoshis); - len += iguana_rwnum(rwflag,&opretbuf[len],sizeof(pax->fiatoshis),&pax->fiatoshis); - len += iguana_rwnum(rwflag,&opretbuf[len],sizeof(pax->height),&pax->height); - len += iguana_rwnum(rwflag,&opretbuf[len],sizeof(pax->otherheight),&pax->otherheight); - if ( rwflag != 0 ) - { - memcpy(&opretbuf[len],pax->rmd160,20), len += 20; - for (i=0; i<4; i++) - opretbuf[len++] = pax->source[i]; - } - else - { - memcpy(pax->rmd160,&opretbuf[len],20), len += 20; - for (i=0; i<4; i++) - pax->source[i] = opretbuf[len++]; - } - return(len); -} - -int32_t komodo_issued_opreturn(char *base,uint256 *txids,uint16_t *vouts,int64_t *values,int64_t *srcvalues,int32_t *kmdheights,int32_t *otherheights,int8_t *baseids,uint8_t *rmd160s,uint8_t *opretbuf,int32_t opretlen,int32_t iskomodo) -{ - struct pax_transaction p,*pax; int32_t i,n=0,j,len=0,incr,height,otherheight; uint8_t type,rmd160[20]; uint64_t fiatoshis; char symbol[KOMODO_ASSETCHAIN_MAXLEN]; - //if ( KOMODO_PAX == 0 ) - // return(0); - incr = 34 + (iskomodo * (2*sizeof(fiatoshis) + 2*sizeof(height) + 20 + 4)); - //41e77b91cb68dc2aa02fa88550eae6b6d44db676a7e935337b6d1392d9718f03cb0200305c90660400000000fbcbeb1f000000bde801006201000058e7945ad08ddba1eac9c9b6c8e1e97e8016a2d152 - - // 41e94d736ec69d88c08b5d238abeeca609c02357a8317e0d56c328bcb1c259be5d0200485bc80200000000404b4c000000000059470200b80b000061f22ba7d19fe29ac3baebd839af8b7127d1f9075553440046bb4cc7a3b5cd39dffe7206507a3482a00780e617f68b273cce9817ed69298d02001069ca1b0000000080f0fa02000000005b470200b90b000061f22ba7d19fe29ac3baebd839af8b7127d1f90755 - - //for (i=0; i>>>>>> %s: (%s) fiat %.8f kmdheight.%d other.%d -> %s %.8f\n",type=='A'?"approvedA":"issuedX",baseids[n]>=0?CURRENCIES[baseids[n]]:"???",dstr(p.fiatoshis),kmdheights[n],otherheights[n],coinaddr,dstr(values[n])); - } - } - } - else - { - for (i=0; i<4; i++) - base[i] = opretbuf[opretlen-4+i]; - for (j=0; j<32; j++) - { - ((uint8_t *)&txids[n])[j] = opretbuf[len++]; - //printf("%02x",((uint8_t *)&txids[n])[j]); - } - vouts[n] = opretbuf[len++]; - vouts[n] = (opretbuf[len++] << 8) | vouts[n]; - baseids[n] = komodo_baseid(base); - if ( (pax= komodo_paxfinds(txids[n],vouts[n])) != 0 ) - { - values[n] = (strcmp("KMD",base) == 0) ? pax->komodoshis : pax->fiatoshis; - srcvalues[n] = (strcmp("KMD",base) == 0) ? pax->fiatoshis : pax->komodoshis; - kmdheights[n] = pax->height; - otherheights[n] = pax->otherheight; - memcpy(&rmd160s[n * 20],pax->rmd160,20); - } - } - //printf(" komodo_issued_opreturn issuedtxid v%d i.%d opretlen.%d\n",vouts[n],n,opretlen); - } - } - return(n); -} - -int32_t komodo_paxcmp(char *symbol,int32_t kmdheight,uint64_t value,uint64_t checkvalue,uint64_t seed) -{ - int32_t ratio; - if ( seed == 0 && checkvalue != 0 ) - { - ratio = ((value << 6) / checkvalue); - if ( ratio >= 60 && ratio <= 67 ) - return(0); - else - { - if ( ASSETCHAINS_SYMBOL[0] != 0 ) - printf("ht.%d ignore mismatched %s value %lld vs checkvalue %lld -> ratio.%d\n",kmdheight,symbol,(long long)value,(long long)checkvalue,ratio); - return(-1); - } - } - else if ( checkvalue != 0 ) - { - ratio = ((value << 10) / checkvalue); - if ( ratio >= 1023 && ratio <= 1025 ) - return(0); - } - return(value != checkvalue); -} - -uint64_t komodo_paxtotal() -{ - struct pax_transaction *pax,*pax2,*tmp,*tmp2; char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN],*str; int32_t i,ht; int64_t checktoshis; uint64_t seed,total = 0; struct komodo_state *basesp; - if ( KOMODO_PASSPORT_INITDONE == 0 ) //KOMODO_PAX == 0 || - return(0); - if ( komodo_isrealtime(&ht) == 0 ) - return(0); - else - { - HASH_ITER(hh,PAX,pax,tmp) - { - if ( pax->marked != 0 ) - continue; - if ( pax->type == 'A' || pax->type == 'D' || pax->type == 'X' ) - str = pax->symbol; - else str = pax->source; - basesp = komodo_stateptrget(str); - if ( basesp != 0 && pax->didstats == 0 ) - { - if ( pax->type == 'I' && (pax2= komodo_paxfind(pax->txid,pax->vout,'D')) != 0 ) - { - if ( pax2->fiatoshis != 0 ) - { - pax->komodoshis = pax2->komodoshis; - pax->fiatoshis = pax2->fiatoshis; - basesp->issued += pax->fiatoshis; - pax->didstats = 1; - if ( strcmp(str,ASSETCHAINS_SYMBOL) == 0 ) - printf("########### %p issued %s += %.8f kmdheight.%d %.8f other.%d\n",basesp,str,dstr(pax->fiatoshis),pax->height,dstr(pax->komodoshis),pax->otherheight); - pax2->marked = pax->height; - pax->marked = pax->height; - } - } - else if ( pax->type == 'W' ) - { - //bitcoin_address(coinaddr,addrtype,rmd160,20); - if ( (checktoshis= komodo_paxprice(&seed,pax->height,pax->source,(char *)"KMD",(uint64_t)pax->fiatoshis)) != 0 ) - { - if ( komodo_paxcmp(pax->source,pax->height,pax->komodoshis,checktoshis,seed) != 0 ) - { - pax->marked = pax->height; - //printf("WITHDRAW.%s mark <- %d %.8f != %.8f\n",pax->source,pax->height,dstr(checktoshis),dstr(pax->komodoshis)); - } - else if ( pax->validated == 0 ) - { - pax->validated = pax->komodoshis = checktoshis; - //int32_t j; for (j=0; j<32; j++) - // printf("%02x",((uint8_t *)&pax->txid)[j]); - //if ( strcmp(str,ASSETCHAINS_SYMBOL) == 0 ) - // printf(" v%d %p got WITHDRAW.%s kmd.%d ht.%d %.8f -> %.8f/%.8f\n",pax->vout,pax,pax->source,pax->height,pax->otherheight,dstr(pax->fiatoshis),dstr(pax->komodoshis),dstr(checktoshis)); - } - } - } - } - } - } - komodo_stateptr(symbol,dest); - HASH_ITER(hh,PAX,pax,tmp) - { - pax->ready = 0; - if ( 0 && pax->type == 'A' ) - printf("%p pax.%s <- %s marked.%d %.8f -> %.8f validated.%d approved.%d\n",pax,pax->symbol,pax->source,pax->marked,dstr(pax->komodoshis),dstr(pax->fiatoshis),pax->validated != 0,pax->approved != 0); - if ( pax->marked != 0 ) - continue; - if ( strcmp(symbol,pax->symbol) == 0 || pax->type == 'A' ) - { - if ( pax->marked == 0 ) - { - if ( komodo_is_issuer() != 0 ) - { - if ( pax->validated != 0 && pax->type == 'D' ) - { - total += pax->fiatoshis; - pax->ready = 1; - } - } - else if ( pax->approved != 0 && pax->type == 'A' ) - { - if ( pax->validated != 0 ) - { - total += pax->komodoshis; - pax->ready = 1; - } - else - { - seed = 0; - checktoshis = komodo_paxprice(&seed,pax->height,pax->source,(char *)"KMD",(uint64_t)pax->fiatoshis); - //printf("paxtotal PAX_fiatdest ht.%d price %s %.8f -> KMD %.8f vs %.8f\n",pax->height,pax->symbol,(double)pax->fiatoshis/COIN,(double)pax->komodoshis/COIN,(double)checktoshis/COIN); - //printf(" v%d %.8f k.%d ht.%d\n",pax->vout,dstr(pax->komodoshis),pax->height,pax->otherheight); - if ( seed != 0 && checktoshis != 0 ) - { - if ( checktoshis == pax->komodoshis ) - { - total += pax->komodoshis; - pax->validated = pax->komodoshis; - pax->ready = 1; - } else pax->marked = pax->height; - } - } - } - if ( 0 && pax->ready != 0 ) - printf("%p (%c) pax.%s marked.%d %.8f -> %.8f validated.%d approved.%d ready.%d ht.%d\n",pax,pax->type,pax->symbol,pax->marked,dstr(pax->komodoshis),dstr(pax->fiatoshis),pax->validated != 0,pax->approved != 0,pax->ready,pax->height); - } - } - } - //printf("paxtotal %.8f\n",dstr(total)); - return(total); -} - -static int _paxorder(const void *a,const void *b) -{ -#define pax_a (*(struct pax_transaction **)a) -#define pax_b (*(struct pax_transaction **)b) - uint64_t aval,bval; - aval = pax_a->fiatoshis + pax_a->komodoshis + pax_a->height; - bval = pax_b->fiatoshis + pax_b->komodoshis + pax_b->height; - if ( bval > aval ) - return(-1); - else if ( bval < aval ) - return(1); - return(0); -#undef pax_a -#undef pax_b -} - -int32_t komodo_pending_withdraws(char *opretstr) // todo: enforce deterministic order -{ - struct pax_transaction *pax,*pax2,*tmp,*paxes[64]; uint8_t opretbuf[16384*4]; int32_t i,n,ht,len=0; uint64_t total = 0; - if ( KOMODO_PAX == 0 || KOMODO_PASSPORT_INITDONE == 0 ) - return(0); - if ( komodo_isrealtime(&ht) == 0 || ASSETCHAINS_SYMBOL[0] != 0 ) - return(0); - n = 0; - HASH_ITER(hh,PAX,pax,tmp) - { - if ( pax->type == 'W' ) - { - if ( (pax2= komodo_paxfind(pax->txid,pax->vout,'A')) != 0 ) - { - if ( pax2->approved != 0 ) - pax->approved = pax2->approved; - } - else if ( (pax2= komodo_paxfind(pax->txid,pax->vout,'X')) != 0 ) - pax->approved = pax->height; - //printf("pending_withdraw: pax %s marked.%u approved.%u validated.%llu\n",pax->symbol,pax->marked,pax->approved,(long long)pax->validated); - if ( pax->marked == 0 && pax->approved == 0 && pax->validated != 0 ) //strcmp((char *)"KMD",pax->symbol) == 0 && - { - if ( n < sizeof(paxes)/sizeof(*paxes) ) - { - paxes[n++] = pax; - //int32_t j; for (j=0; j<32; j++) - // printf("%02x",((uint8_t *)&pax->txid)[j]); - //printf(" %s.(kmdht.%d ht.%d marked.%u approved.%d validated %.8f) %.8f\n",pax->source,pax->height,pax->otherheight,pax->marked,pax->approved,dstr(pax->validated),dstr(pax->komodoshis)); - } - } - } - } - opretstr[0] = 0; - if ( n > 0 ) - { - opretbuf[len++] = 'A'; - qsort(paxes,n,sizeof(*paxes),_paxorder); - for (i=0; i>3)*7 ) - len += komodo_rwapproval(1,&opretbuf[len],paxes[i]); - } - if ( len > 0 ) - init_hexbytes_noT(opretstr,opretbuf,len); - } - //fprintf(stderr,"komodo_pending_withdraws len.%d PAXTOTAL %.8f\n",len,dstr(komodo_paxtotal())); - return(len); -} - -int32_t komodo_gateway_deposits(CMutableTransaction *txNew,char *base,int32_t tokomodo) -{ - struct pax_transaction *pax,*tmp; char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; uint8_t *script,opcode,opret[16384*4],data[16384*4]; int32_t i,baseid,ht,len=0,opretlen=0,numvouts=1; struct komodo_state *sp; uint64_t available,deposited,issued,withdrawn,approved,redeemed,mask,sum = 0; - if ( KOMODO_PASSPORT_INITDONE == 0 )//KOMODO_PAX == 0 || - return(0); - struct komodo_state *kmdsp = komodo_stateptrget((char *)"KMD"); - sp = komodo_stateptr(symbol,dest); - strcpy(symbol,base); - if ( ASSETCHAINS_SYMBOL[0] != 0 && komodo_baseid(ASSETCHAINS_SYMBOL) < 0 ) - return(0); - PENDING_KOMODO_TX = 0; - for (i=0; i<3; i++) - { - if ( komodo_isrealtime(&ht) != 0 ) - break; - sleep(1); - } - if ( i == 3 ) - { - if ( tokomodo == 0 ) - printf("%s not realtime ht.%d\n",ASSETCHAINS_SYMBOL,ht); - return(0); - } - if ( tokomodo == 0 ) - { - opcode = 'I'; - } - else - { - opcode = 'X'; - if ( 1 || komodo_paxtotal() == 0 ) - return(0); - } - HASH_ITER(hh,PAX,pax,tmp) - { - if ( pax->type != 'D' && pax->type != 'A' ) - continue; - { -#ifdef KOMODO_ASSETCHAINS_WAITNOTARIZE - if ( pax->height > 236000 ) - { - if ( kmdsp != 0 && kmdsp->NOTARIZED_HEIGHT >= pax->height ) - pax->validated = pax->komodoshis; - else if ( kmdsp->CURRENT_HEIGHT > pax->height+30 ) - pax->validated = pax->ready = 0; - } - else - { - if ( kmdsp != 0 && (kmdsp->NOTARIZED_HEIGHT >= pax->height || kmdsp->CURRENT_HEIGHT > pax->height+30) ) // assumes same chain as notarize - pax->validated = pax->komodoshis; - else pax->validated = pax->ready = 0; - } -#else - pax->validated = pax->komodoshis; -#endif - } - if ( ASSETCHAINS_SYMBOL[0] != 0 && (pax_fiatstatus(&available,&deposited,&issued,&withdrawn,&approved,&redeemed,symbol) != 0 || available < pax->fiatoshis) ) - { - //if ( pax->height > 214700 || strcmp(ASSETCHAINS_SYMBOL,symbol) == 0 ) - // printf("miner.[%s]: skip %s %.8f when avail %.8f deposited %.8f, issued %.8f withdrawn %.8f approved %.8f redeemed %.8f\n",ASSETCHAINS_SYMBOL,symbol,dstr(pax->fiatoshis),dstr(available),dstr(deposited),dstr(issued),dstr(withdrawn),dstr(approved),dstr(redeemed)); - continue; - } - /*printf("pax.%s marked.%d %.8f -> %.8f ready.%d validated.%d\n",pax->symbol,pax->marked,dstr(pax->komodoshis),dstr(pax->fiatoshis),pax->ready!=0,pax->validated!=0); - if ( pax->marked != 0 || (pax->type != 'D' && pax->type != 'A') || pax->ready == 0 ) - { - printf("reject 2\n"); - continue; - }*/ - if ( ASSETCHAINS_SYMBOL[0] != 0 && (strcmp(pax->symbol,symbol) != 0 || pax->validated == 0 || pax->ready == 0) ) - { - if ( strcmp(pax->symbol,ASSETCHAINS_SYMBOL) == 0 ) - printf("pax->symbol.%s != %s or null pax->validated %.8f ready.%d ht.(%d %d)\n",pax->symbol,symbol,dstr(pax->validated),pax->ready,kmdsp->CURRENT_HEIGHT,pax->height); - pax->marked = pax->height; - continue; - } - if ( pax->ready == 0 ) - continue; - if ( pax->type == 'A' && ASSETCHAINS_SYMBOL[0] == 0 ) - { - if ( kmdsp != 0 ) - { - if ( (baseid= komodo_baseid(pax->symbol)) < 0 || ((1LL << baseid) & sp->RTmask) == 0 ) - { - printf("not RT for (%s) %llx baseid.%d %llx\n",pax->symbol,(long long)sp->RTmask,baseid,(long long)(1LL< %.8f ready.%d validated.%d approved.%d\n",tokomodo,pax->type,pax,pax->symbol,pax->marked,dstr(pax->komodoshis),dstr(pax->fiatoshis),pax->ready!=0,pax->validated!=0,pax->approved!=0); - if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) - printf("pax.%s marked.%d %.8f -> %.8f\n",ASSETCHAINS_SYMBOL,pax->marked,dstr(pax->komodoshis),dstr(pax->fiatoshis)); - if ( opcode == 'I' ) - { - sum += pax->fiatoshis; - if ( sum > available ) - break; - } - txNew->vout.resize(numvouts+1); - txNew->vout[numvouts].nValue = (opcode == 'I') ? pax->fiatoshis : pax->komodoshis; - txNew->vout[numvouts].scriptPubKey.resize(25); - script = (uint8_t *)&txNew->vout[numvouts].scriptPubKey[0]; - *script++ = 0x76; - *script++ = 0xa9; - *script++ = 20; - memcpy(script,pax->rmd160,20), script += 20; - *script++ = 0x88; - *script++ = 0xac; - if ( tokomodo == 0 ) - { - for (i=0; i<32; i++) - data[len++] = ((uint8_t *)&pax->txid)[i]; - data[len++] = pax->vout & 0xff; - data[len++] = (pax->vout >> 8) & 0xff; - PENDING_KOMODO_TX += pax->fiatoshis; - } - else - { - len += komodo_rwapproval(1,&data[len],pax); - PENDING_KOMODO_TX += pax->komodoshis; - printf(" len.%d vout.%u DEPOSIT %.8f <- pax.%s pending ht %d %d %.8f | ",len,pax->vout,(double)txNew->vout[numvouts].nValue/COIN,symbol,pax->height,pax->otherheight,dstr(PENDING_KOMODO_TX)); - } - if ( numvouts++ >= 64 || sum > COIN ) - break; - } - if ( numvouts > 1 ) - { - if ( tokomodo != 0 ) - strcpy(symbol,(char *)"KMD"); - for (i=0; symbol[i]!=0; i++) - data[len++] = symbol[i]; - data[len++] = 0; - for (i=0; ivout.resize(numvouts+1); - txNew->vout[numvouts].nValue = 0; - txNew->vout[numvouts].scriptPubKey.resize(opretlen); - script = (uint8_t *)&txNew->vout[numvouts].scriptPubKey[0]; - memcpy(script,opret,opretlen); - for (i=0; i<8; i++) - printf("%02x",opret[i]); - printf(" <- opret, MINER deposits.%d (%s) vouts.%d %.8f opretlen.%d\n",tokomodo,ASSETCHAINS_SYMBOL,numvouts,dstr(PENDING_KOMODO_TX),opretlen); - return(1); - } - return(0); -} - -const char *banned_txids[] = -{ - "78cb4e21245c26b015b888b14c4f5096e18137d2741a6de9734d62b07014dfca", // vout1 only 233559 - "00697be658e05561febdee1aafe368b821ca33fbb89b7027365e3d77b5dfede5", //234172 - "e909465788b32047c472d73e882d79a92b0d550f90be008f76e1edaee6d742ea", //234187 - "f56c6873748a327d0b92b8108f8ec8505a2843a541b1926022883678fb24f9dc", //234188 - "abf08be07d8f5b3a433ddcca7ef539e79a3571632efd6d0294ec0492442a0204", //234213 - "3b854b996cc982fba8c06e76cf507ae7eed52ab92663f4c0d7d10b3ed879c3b0", //234367 - "fa9e474c2cda3cb4127881a40eb3f682feaba3f3328307d518589024a6032cc4", //234635 - "ca746fa13e0113c4c0969937ea2c66de036d20274efad4ce114f6b699f1bc0f3", //234662 - "43ce88438de4973f21b1388ffe66e68fda592da38c6ef939be10bb1b86387041", //234697 - "0aeb748de82f209cd5ff7d3a06f65543904c4c17387c9d87c65fd44b14ad8f8c", //234899 - "bbd3a3d9b14730991e1066bd7c626ca270acac4127131afe25f877a5a886eb25", //235252 - "fa9943525f2e6c32cbc243294b08187e314d83a2870830180380c3c12a9fd33c", //235253 - "a01671c8775328a41304e31a6693bbd35e9acbab28ab117f729eaba9cb769461", //235265 - "2ef49d2d27946ad7c5d5e4ab5c089696762ff04e855f8ab48e83bdf0cc68726d", //235295 - "c85dcffb16d5a45bd239021ad33443414d60224760f11d535ae2063e5709efee", //235296 - // all vouts banned - "c4ea1462c207547cd6fb6a4155ca6d042b22170d29801a465db5c09fec55b19d", //246748 - "305dc96d8bc23a69d3db955e03a6a87c1832673470c32fe25473a46cc473c7d1", //247204 - //"43416a0c4da6b1a5c1d375bdbe8f7dc8d44d8f60df593d3376aa8221ec66357e", // vout0 only - //"1eb295ed54c47f35cbccd7e7e40d03041f1853581da6d41102a9d8813782b6cb", - //"db121e4012222adfc841824984a2a90b7e5b018dd71307822537d58160195e43", - //"28f95b8148ac4ae6e09c7380e34422fab41d568a411e53dc94823e36a3d6f386", - //"01d8c839463bda2f2f6400ede4611357913684927a767422a8560ead1b22557c", - //"6e4980a9e1bd669f4df04732dc6f11b7773b6de88d1abcf89a6b9007d72ef9ac", - //"6cc1d0495170bc0e11fd3925297623562e529ea1336b66ea61f8a1159041aed2", - //"250875424cece9bcd98cb226b09da7671625633d6958589e3a462bad89ad87cc", // missed - //"ea8659011de52f4dac42cda12326064b7b5013b8492f88e33159884ca299aa05", // missed - //"ce567928b5490a17244167af161b1d8dd6ff753fef222fe6855d95b2278a35b3", // missed -}; - -int32_t komodo_checkvout(int32_t vout,int32_t k,int32_t indallvouts) -{ - if ( k < indallvouts ) - return(vout == 1); - else if ( k == indallvouts || k == indallvouts+1 ) - return(1); - else return(vout == 0); -} - -int32_t komodo_bannedset(int32_t *indallvoutsp,uint256 *array,int32_t max) -{ - int32_t i; - if ( sizeof(banned_txids)/sizeof(*banned_txids) > max ) - { - fprintf(stderr,"komodo_bannedset: buffer too small %d vs %d\n",(int32_t)(sizeof(banned_txids)/sizeof(*banned_txids)),max); - StartShutdown(); - } - for (i=0; i 1 && block.vtx[txn_count-1].vout.size() > 0 && block.vtx[txn_count-1].vout[0].nValue == 5000 ) - { - /* - if ( block.vtx[txn_count-1].vin.size() == 1 && GetTransaction(block.vtx[txn_count-1].vin[0].prevout.hash,tx,hash,false) && block.vtx[0].vout[0].scriptPubKey == tx.vout[block.vtx[txn_count-1].vin[0].prevout.n].scriptPubKey ) - notmatched = 1; - */ - if ( block.vtx[txn_count-1].vin.size() == 1 ) { - uint256 hashNotaryProofVin = block.vtx[txn_count-1].vin[0].prevout.hash; - int fNotaryProofVinTxFound = GetTransaction(hashNotaryProofVin,tx,hash,false); - if (!fNotaryProofVinTxFound) { - // try to search in the same block - BOOST_FOREACH(const CTransaction &txInThisBlock, block.vtx) { - if (txInThisBlock.GetHash() == hashNotaryProofVin) { - fNotaryProofVinTxFound = 1; - tx = txInThisBlock; - hash = block.GetHash(); - break; - } - } - } - if ( fNotaryProofVinTxFound && block.vtx[0].vout[0].scriptPubKey == tx.vout[block.vtx[txn_count-1].vin[0].prevout.n].scriptPubKey ) - { - notmatched = 1; - } - } - } - n = block.vtx[i].vin.size(); - for (j=0; j= indallvouts) ) - { - printf("banned tx.%d being used at ht.%d txi.%d vini.%d\n",k,height,i,j); - return(-1); - } - } - } - } - } - if ( height > 0 && ASSETCHAINS_MARMARA != 0 && (height & 1) == 0 ) - { - if ( MarmaraValidateCoinbase(height,block.vtx[0]) < 0 ) - { - fprintf(stderr,"MARMARA error ht.%d constrains even height blocks to pay 100%% to CC in vout0 with opreturn\n",height); - return(-1); - } - } - // we don't want these checks in VRSC, leave it at the Sapling upgrade - if ( ASSETCHAINS_SYMBOL[0] == 0 || - ((ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD) && height > 1) || - NetworkUpgradeActive(height, Params().GetConsensus(), Consensus::UPGRADE_SAPLING) ) - { - n = block.vtx[0].vout.size(); - int64_t val,prevtotal = 0; int32_t strangeout=0,overflow = 0; - total = 0; - for (i=1; i= MAX_MONEY ) - { - overflow = 1; - break; - } - if ( i > 1 && script[0] != 0x6a && val < 5000 ) - strangeout++; - total += val; - if ( total < prevtotal || (val != 0 && total == prevtotal) ) - { - overflow = 1; - break; - } - prevtotal = total; - } - if ( ASSETCHAINS_SYMBOL[0] == 0 ) - { - if ( overflow != 0 || total > COIN/10 ) - { - if ( height >= activation ) - { - if ( height > 800000 ) - fprintf(stderr,">>>>>>>> <<<<<<<<<< ht.%d illegal nonz output %.8f n.%d\n",height,dstr(block.vtx[0].vout[1].nValue),n); - return(-1); - } - } - else if ( block.nBits == KOMODO_MINDIFF_NBITS && total > 0 ) // to deal with fee stealing - { - fprintf(stderr,"notary mined ht.%d with extra %.8f\n",height,dstr(total)); - if ( height > KOMODO_NOTARIES_HEIGHT1 ) - return(-1); - } - if ( strangeout != 0 || notmatched != 0 ) - { - if ( 0 && strcmp(NOTARY_PUBKEY.c_str(),"03b7621b44118017a16043f19b30cc8a4cfe068ac4e42417bae16ba460c80f3828") == 0 ) - fprintf(stderr,">>>>>>>>>>>>> DUST ht.%d strangout.%d notmatched.%d <<<<<<<<<\n",height,strangeout,notmatched); - if ( height > 1000000 && strangeout != 0 ) - return(-1); - } - else if ( height > 814000 ) - { - script = (uint8_t *)&block.vtx[0].vout[0].scriptPubKey[0]; - //int32_t notary = komodo_electednotary(&num,script+1,height,0); - //if ( (-1 * (komodo_electednotary(&num,script+1,height,0) >= 0) * (height > 1000000)) < 0 ) - // fprintf(stderr, ">>>>>>> FAILED BLOCK.%d notary.%d insync.%d\n",height,notary,KOMODO_INSYNC); - //else - // fprintf(stderr, "<<<<<<< VALID BLOCK.%d notary.%d insync.%d\n",height,notary,KOMODO_INSYNC); - return(-1 * (komodo_electednotary(&num,script+1,height,0) >= 0) * (height > 1000000)); - } - } - else - { - checktoshis = 0; - if ( (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD) && height > 1 ) - { - if ( (checktoshis= komodo_checkcommission((CBlock *)&block,height)) < 0 ) - { - fprintf(stderr,"ht.%d checktoshis %.8f overflow.%d total %.8f strangeout.%d\n",height,dstr(checktoshis),overflow,dstr(total),strangeout); - return(-1); - } - } - if ( height > 1 && checktoshis == 0 ) - { - checktoshis = ((uint64_t)GetBlockSubsidy(height, Params().GetConsensus()) - block.vtx[0].vout[0].nValue); - // some pools will need to change their pool fee to be (poolfee % - txfees) - //checktoshis += txn_count * 0.001; // rely on higher level validations to prevent emitting more coins than actual txfees - } - if ( height >= 2 && (overflow != 0 || total > checktoshis || strangeout != 0) ) - { - fprintf(stderr,"checkdeposit: ht.%d checktoshis %.8f overflow.%d total %.8f strangeout.%d\n",height,dstr(checktoshis),overflow,dstr(total),strangeout); - if ( strangeout != 0 ) - fprintf(stderr,">>>>>>>>>>>>> %s DUST ht.%d strangeout.%d notmatched.%d <<<<<<<<<\n",ASSETCHAINS_SYMBOL,height,strangeout,notmatched); - return(-1); - } - } - } - return(0); -} - -const char *komodo_opreturn(int32_t height,uint64_t value,uint8_t *opretbuf,int32_t opretlen,uint256 txid,uint16_t vout,char *source) -{ - uint8_t rmd160[20],rmd160s[64*20],addrtype,shortflag,pubkey33[33]; int32_t didstats,i,j,n,kvheight,len,tokomodo,kmdheight,otherheights[64],kmdheights[64]; int8_t baseids[64]; char base[4],coinaddr[64],destaddr[64]; uint256 txids[64]; uint16_t vouts[64]; uint64_t convtoshis,seed; int64_t fee,fiatoshis,komodoshis,checktoshis,values[64],srcvalues[64]; struct pax_transaction *pax,*pax2; struct komodo_state *basesp; double diff; - const char *typestr = "unknown"; - if ( ASSETCHAINS_SYMBOL[0] != 0 && komodo_baseid(ASSETCHAINS_SYMBOL) < 0 && opretbuf[0] != 'K' ) - { - //printf("komodo_opreturn skip %s\n",ASSETCHAINS_SYMBOL); - return("assetchain"); - } - memset(baseids,0xff,sizeof(baseids)); - memset(values,0,sizeof(values)); - memset(srcvalues,0,sizeof(srcvalues)); - memset(rmd160s,0,sizeof(rmd160s)); - memset(kmdheights,0,sizeof(kmdheights)); - memset(otherheights,0,sizeof(otherheights)); - tokomodo = (komodo_is_issuer() == 0); - if ( opretbuf[0] == 'K' && opretlen != 40 ) - { - komodo_kvupdate(opretbuf,opretlen,value); - return("kv"); - } - else if ( ASSETCHAINS_SYMBOL[0] == 0 && KOMODO_PAX == 0 ) - return("nopax"); - if ( opretbuf[0] == 'D' ) - { - tokomodo = 0; - if ( opretlen == 38 ) // any KMD tx - { - iguana_rwnum(0,&opretbuf[34],sizeof(kmdheight),&kmdheight); - memset(base,0,sizeof(base)); - PAX_pubkey(0,&opretbuf[1],&addrtype,rmd160,base,&shortflag,&fiatoshis); - bitcoin_address(coinaddr,addrtype,rmd160,20); - checktoshis = PAX_fiatdest(&seed,tokomodo,destaddr,pubkey33,coinaddr,kmdheight,base,fiatoshis); - if ( komodo_paxcmp(base,kmdheight,value,checktoshis,kmdheight < 225000 ? seed : 0) != 0 ) - checktoshis = PAX_fiatdest(&seed,tokomodo,destaddr,pubkey33,coinaddr,height,base,fiatoshis); - typestr = "deposit"; - if ( 0 && strcmp("NOK",base) == 0 ) - { - printf("[%s] %s paxdeposit height.%d vs kmdheight.%d\n",ASSETCHAINS_SYMBOL,base,height,kmdheight); - printf("(%s) (%s) kmdheight.%d vs height.%d check %.8f vs %.8f tokomodo.%d %d seed.%llx\n",ASSETCHAINS_SYMBOL,base,kmdheight,height,dstr(checktoshis),dstr(value),komodo_is_issuer(),strncmp(ASSETCHAINS_SYMBOL,base,strlen(base)) == 0,(long long)seed); - for (i=0; i<32; i++) - printf("%02x",((uint8_t *)&txid)[i]); - printf(" <- txid.v%u ",vout); - for (i=0; i<33; i++) - printf("%02x",pubkey33[i]); - printf(" checkpubkey check %.8f v %.8f dest.(%s) kmdheight.%d height.%d\n",dstr(checktoshis),dstr(value),destaddr,kmdheight,height); - } - if ( strcmp(base,ASSETCHAINS_SYMBOL) == 0 && (kmdheight > 195000 || kmdheight <= height) ) - { - didstats = 0; - if ( komodo_paxcmp(base,kmdheight,value,checktoshis,kmdheight < 225000 ? seed : 0) == 0 ) - { - if ( (pax= komodo_paxfind(txid,vout,'D')) == 0 ) - { - if ( (basesp= komodo_stateptrget(base)) != 0 ) - { - basesp->deposited += fiatoshis; - didstats = 1; - if ( 0 && strcmp(base,ASSETCHAINS_SYMBOL) == 0 ) - printf("########### %p deposited %s += %.8f kmdheight.%d %.8f\n",basesp,base,dstr(fiatoshis),kmdheight,dstr(value)); - } else printf("cant get stateptr.(%s)\n",base); - komodo_gateway_deposit(coinaddr,value,base,fiatoshis,rmd160,txid,vout,'D',kmdheight,height,(char *)"KMD",0); - } - if ( (pax= komodo_paxfind(txid,vout,'D')) != 0 ) - { - pax->height = kmdheight; - pax->validated = value; - pax->komodoshis = value; - pax->fiatoshis = fiatoshis; - if ( didstats == 0 && pax->didstats == 0 ) - { - if ( (basesp= komodo_stateptrget(base)) != 0 ) - { - basesp->deposited += fiatoshis; - didstats = 1; - if ( 0 && strcmp(base,ASSETCHAINS_SYMBOL) == 0 ) - printf("########### %p depositedB %s += %.8f/%.8f kmdheight.%d/%d %.8f/%.8f\n",basesp,base,dstr(fiatoshis),dstr(pax->fiatoshis),kmdheight,pax->height,dstr(value),dstr(pax->komodoshis)); - } - } // - if ( didstats != 0 ) - pax->didstats = 1; - if ( (pax2= komodo_paxfind(txid,vout,'I')) != 0 ) - { - pax2->fiatoshis = pax->fiatoshis; - pax2->komodoshis = pax->komodoshis; - pax->marked = pax2->marked = pax->height; - pax2->height = pax->height = height; - if ( pax2->didstats == 0 ) - { - if ( (basesp= komodo_stateptrget(base)) != 0 ) - { - basesp->issued += pax2->fiatoshis; - pax2->didstats = 1; - if ( 0 && strcmp(base,"USD") == 0 ) - printf("########### %p issueda %s += %.8f kmdheight.%d %.8f other.%d [%d]\n",basesp,base,dstr(pax2->fiatoshis),pax2->height,dstr(pax2->komodoshis),pax2->otherheight,height); - } - } - } - } - } - else - { - if ( (pax= komodo_paxfind(txid,vout,'D')) != 0 ) - pax->marked = checktoshis; - if ( kmdheight > 238000 && (kmdheight > 214700 || strcmp(base,ASSETCHAINS_SYMBOL) == 0) ) //seed != 0 && - printf("pax %s deposit %.8f rejected kmdheight.%d %.8f KMD check %.8f seed.%llu\n",base,dstr(fiatoshis),kmdheight,dstr(value),dstr(checktoshis),(long long)seed); - } - } //else printf("[%s] %s paxdeposit height.%d vs kmdheight.%d\n",ASSETCHAINS_SYMBOL,base,height,kmdheight); - } //else printf("unsupported size.%d for opreturn D\n",opretlen); - } - else if ( opretbuf[0] == 'I' ) - { - tokomodo = 0; - if ( strncmp((char *)"KMD",(char *)&opretbuf[opretlen-4],3) != 0 && strncmp(ASSETCHAINS_SYMBOL,(char *)&opretbuf[opretlen-4],3) == 0 ) - { - if ( (n= komodo_issued_opreturn(base,txids,vouts,values,srcvalues,kmdheights,otherheights,baseids,rmd160s,opretbuf,opretlen,0)) > 0 ) - { - for (i=0; itype = opretbuf[0]; - strcpy(pax->source,(char *)&opretbuf[opretlen-4]); - if ( (pax2= komodo_paxfind(txids[i],vouts[i],'D')) != 0 && pax2->fiatoshis != 0 && pax2->komodoshis != 0 ) - { - // realtime path? - pax->fiatoshis = pax2->fiatoshis; - pax->komodoshis = pax2->komodoshis; - pax->marked = pax2->marked = pax2->height; - if ( pax->didstats == 0 ) - { - if ( (basesp= komodo_stateptrget(CURRENCIES[baseids[i]])) != 0 ) - { - basesp->issued += pax->fiatoshis; - pax->didstats = 1; - pax->height = pax2->height; - pax->otherheight = height; - if ( 1 && strcmp(CURRENCIES[baseids[i]],"USD") == 0 ) - printf("########### %p issuedb %s += %.8f kmdheight.%d %.8f other.%d [%d]\n",basesp,CURRENCIES[baseids[i]],dstr(pax->fiatoshis),pax->height,dstr(pax->komodoshis),pax->otherheight,height); - } - } - } - } - if ( (pax= komodo_paxmark(height,txids[i],vouts[i],'I',height)) != 0 ) - komodo_paxdelete(pax); - if ( (pax= komodo_paxmark(height,txids[i],vouts[i],'D',height)) != 0 ) - komodo_paxdelete(pax); - } - } //else printf("opreturn none issued?\n"); - } - } - else if ( height < 236000 && opretbuf[0] == 'W' && strncmp(ASSETCHAINS_SYMBOL,(char *)&opretbuf[opretlen-4],3) == 0 )//&& opretlen >= 38 ) - { - if ( komodo_baseid((char *)&opretbuf[opretlen-4]) >= 0 && strcmp(ASSETCHAINS_SYMBOL,(char *)&opretbuf[opretlen-4]) == 0 ) - { - for (i=0; i (%s) len.%d\n",ASSETCHAINS_SYMBOL,base,kmdheight,height,dstr(checktoshis),dstr(komodoshis),dstr(value),komodo_is_issuer(),strncmp(ASSETCHAINS_SYMBOL,base,strlen(base)) == 0,(long long)seed,coinaddr,opretlen); - didstats = 0; - //if ( komodo_paxcmp(base,kmdheight,komodoshis,checktoshis,seed) == 0 ) - { - if ( value != 0 && ((pax= komodo_paxfind(txid,vout,'W')) == 0 || pax->didstats == 0) ) - { - if ( (basesp= komodo_stateptrget(base)) != 0 ) - { - basesp->withdrawn += value; - didstats = 1; - if ( 0 && strcmp(base,ASSETCHAINS_SYMBOL) == 0 ) - printf("########### %p withdrawn %s += %.8f check %.8f\n",basesp,base,dstr(value),dstr(checktoshis)); - } - if ( 0 && strcmp(base,"RUB") == 0 && (pax == 0 || pax->approved == 0) ) - printf("notarize %s %.8f -> %.8f kmd.%d other.%d\n",ASSETCHAINS_SYMBOL,dstr(value),dstr(komodoshis),kmdheight,height); - } - komodo_gateway_deposit(coinaddr,0,(char *)"KMD",value,rmd160,txid,vout,'W',kmdheight,height,source,0); - if ( (pax= komodo_paxfind(txid,vout,'W')) != 0 ) - { - pax->type = opretbuf[0]; - strcpy(pax->source,base); - strcpy(pax->symbol,"KMD"); - pax->height = kmdheight; - pax->otherheight = height; - pax->komodoshis = komodoshis; - } - } // else printf("withdraw %s paxcmp ht.%d %d error value %.8f -> %.8f vs %.8f\n",base,kmdheight,height,dstr(value),dstr(komodoshis),dstr(checktoshis)); - // need to allocate pax - } - else if ( height < 236000 && tokomodo != 0 && opretbuf[0] == 'A' && ASSETCHAINS_SYMBOL[0] == 0 ) - { - tokomodo = 1; - if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) - { - for (i=0; i 0 ) - { - for (i=0; isymbol); - printf("override neg1 with (%s)\n",pax->symbol); - } - if ( baseids[i] < 0 ) - continue; - } - didstats = 0; - seed = 0; - checktoshis = komodo_paxprice(&seed,kmdheights[i],CURRENCIES[baseids[i]],(char *)"KMD",(uint64_t)values[i]); - //printf("PAX_fiatdest ht.%d price %s %.8f -> KMD %.8f vs %.8f\n",kmdheights[i],CURRENCIES[baseids[i]],(double)values[i]/COIN,(double)srcvalues[i]/COIN,(double)checktoshis/COIN); - if ( srcvalues[i] == checktoshis ) - { - if ( (pax= komodo_paxfind(txids[i],vouts[i],'A')) == 0 ) - { - bitcoin_address(coinaddr,60,&rmd160s[i*20],20); - komodo_gateway_deposit(coinaddr,srcvalues[i],CURRENCIES[baseids[i]],values[i],&rmd160s[i*20],txids[i],vouts[i],'A',kmdheights[i],otherheights[i],CURRENCIES[baseids[i]],kmdheights[i]); - if ( (pax= komodo_paxfind(txids[i],vouts[i],'A')) == 0 ) - printf("unexpected null pax for approve\n"); - else pax->validated = checktoshis; - if ( (pax2= komodo_paxfind(txids[i],vouts[i],'W')) != 0 ) - pax2->approved = kmdheights[i]; - komodo_paxmark(height,txids[i],vouts[i],'W',height); - //komodo_paxmark(height,txids[i],vouts[i],'A',height); - if ( values[i] != 0 && (basesp= komodo_stateptrget(CURRENCIES[baseids[i]])) != 0 ) - { - basesp->approved += values[i]; - didstats = 1; - //printf("pax.%p ########### %p approved %s += %.8f -> %.8f/%.8f kht.%d %d\n",pax,basesp,CURRENCIES[baseids[i]],dstr(values[i]),dstr(srcvalues[i]),dstr(checktoshis),kmdheights[i],otherheights[i]); - } - //printf(" i.%d (%s) <- %.8f ADDFLAG APPROVED\n",i,coinaddr,dstr(values[i])); - } - else if ( pax->didstats == 0 && srcvalues[i] != 0 ) - { - if ( (basesp= komodo_stateptrget(CURRENCIES[baseids[i]])) != 0 ) - { - basesp->approved += values[i]; - didstats = 1; - //printf("pax.%p ########### %p approved %s += %.8f -> %.8f/%.8f kht.%d %d\n",pax,basesp,CURRENCIES[baseids[i]],dstr(values[i]),dstr(srcvalues[i]),dstr(checktoshis),kmdheights[i],otherheights[i]); - } - } //else printf(" i.%d of n.%d pax.%p baseids[] %d\n",i,n,pax,baseids[i]); - if ( (pax= komodo_paxfind(txids[i],vouts[i],'A')) != 0 ) - { - pax->type = opretbuf[0]; - pax->approved = kmdheights[i]; - pax->validated = checktoshis; - if ( didstats != 0 ) - pax->didstats = 1; - //if ( strcmp(CURRENCIES[baseids[i]],ASSETCHAINS_SYMBOL) == 0 ) - //printf(" i.%d approved.%d <<<<<<<<<<<<< APPROVED %p\n",i,kmdheights[i],pax); - } - } - } - } //else printf("n.%d from opreturns\n",n); - //printf("extra.[%d] after %.8f\n",n,dstr(komodo_paxtotal())); - } - else if ( height < 236000 && opretbuf[0] == 'X' && ASSETCHAINS_SYMBOL[0] == 0 ) - { - tokomodo = 1; - if ( (n= komodo_issued_opreturn(base,txids,vouts,values,srcvalues,kmdheights,otherheights,baseids,rmd160s,opretbuf,opretlen,1)) > 0 ) - { - for (i=0; itype = opretbuf[0]; - if ( height < 121842 ) // fields got switched around due to legacy issues and approves - value = srcvalues[i]; - else value = values[i]; - if ( baseids[i] >= 0 && value != 0 && (basesp= komodo_stateptrget(CURRENCIES[baseids[i]])) != 0 ) - { - basesp->redeemed += value; - pax->didstats = 1; - if ( strcmp(CURRENCIES[baseids[i]],ASSETCHAINS_SYMBOL) == 0 ) - printf("ht.%d %.8f ########### %p redeemed %s += %.8f %.8f kht.%d ht.%d\n",height,dstr(value),basesp,CURRENCIES[baseids[i]],dstr(value),dstr(srcvalues[i]),kmdheights[i],otherheights[i]); - } - } - if ( (pax= komodo_paxmark(height,txids[i],vouts[i],'W',height)) != 0 ) - komodo_paxdelete(pax); - if ( (pax= komodo_paxmark(height,txids[i],vouts[i],'A',height)) != 0 ) - komodo_paxdelete(pax); - if ( (pax= komodo_paxmark(height,txids[i],vouts[i],'X',height)) != 0 ) - komodo_paxdelete(pax); - } - } //else printf("komodo_issued_opreturn returned %d\n",n); - } - return(typestr); -} - -int32_t komodo_parsestatefiledata(struct komodo_state *sp,uint8_t *filedata,long *fposp,long datalen,char *symbol,char *dest); - -void komodo_stateind_set(struct komodo_state *sp,uint32_t *inds,int32_t n,uint8_t *filedata,long datalen,char *symbol,char *dest) -{ - uint8_t func; long lastK,lastT,lastN,lastV,fpos,lastfpos; int32_t i,count,doissue,iter,numn,numv,numN,numV,numR; uint32_t tmp,prevpos100,offset; - count = numR = numN = numV = numn = numv = 0; - lastK = lastT = lastN = lastV = -1; - for (iter=0; iter<2; iter++) - { - for (lastfpos=fpos=prevpos100=i=0; i> 8); - fpos = prevpos100 + offset; - if ( lastfpos >= datalen || (filedata[lastfpos] != func && func != 0) ) - printf("i.%d/n.%d lastfpos.%ld >= datalen.%ld or [%d] != func.%d\n",i,n,lastfpos,datalen,filedata[lastfpos],func); - else if ( iter == 0 ) - { - switch ( func ) - { - default: case 'P': case 'U': case 'D': - inds[i] &= 0xffffff00; - break; - case 'K': - lastK = lastfpos; - inds[i] &= 0xffffff00; - break; - case 'T': - lastT = lastfpos; - inds[i] &= 0xffffff00; - break; - case 'N': - lastN = lastfpos; - numN++; - break; - case 'V': - lastV = lastfpos; - numV++; - break; - case 'R': - numR++; - break; - } - } - else - { - doissue = 0; - if ( func == 'K' ) - { - if ( lastK == lastfpos ) - doissue = 1; - } - else if ( func == 'T' ) - { - if ( lastT == lastfpos ) - doissue = 1; - } - else if ( func == 'N' ) - { - if ( numn > numN-128 ) - doissue = 1; - numn++; - } - else if ( func == 'V' ) - { - if ( KOMODO_PAX != 0 && numv > numV-1440 ) - doissue = 1; - numv++; - } - else if ( func == 'R' ) - doissue = 1; - if ( doissue != 0 ) - { - //printf("issue %c total.%d lastfpos.%ld\n",func,count,lastfpos); - komodo_parsestatefiledata(sp,filedata,&lastfpos,datalen,symbol,dest); - count++; - } - } - } - lastfpos = fpos; - } - } - printf("numR.%d numV.%d numN.%d count.%d\n",numR,numV,numN,count); - /*else if ( func == 'K' ) // KMD height: stop after 1st - else if ( func == 'T' ) // KMD height+timestamp: stop after 1st - - else if ( func == 'N' ) // notarization, scan backwards 1440+ blocks; - else if ( func == 'V' ) // price feed: can stop after 1440+ - else if ( func == 'R' ) // opreturn:*/ -} - -void *OS_loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) -{ - FILE *fp; - long filesize,buflen = *allocsizep; - uint8_t *buf = *bufp; - *lenp = 0; - if ( (fp= fopen(fname,"rb")) != 0 ) - { - fseek(fp,0,SEEK_END); - filesize = ftell(fp); - if ( filesize == 0 ) - { - fclose(fp); - *lenp = 0; - printf("OS_loadfile null size.(%s)\n",fname); - return(0); - } - if ( filesize > buflen ) - { - *allocsizep = filesize; - *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); - } - rewind(fp); - if ( buf == 0 ) - printf("Null buf ???\n"); - else - { - if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) - printf("error reading filesize.%ld\n",(long)filesize); - buf[filesize] = 0; - } - fclose(fp); - *lenp = filesize; - //printf("loaded.(%s)\n",buf); - } //else printf("OS_loadfile couldnt load.(%s)\n",fname); - return(buf); -} - -uint8_t *OS_fileptr(long *allocsizep,char *fname) -{ - long filesize = 0; uint8_t *buf = 0; void *retptr; - *allocsizep = 0; - retptr = OS_loadfile(fname,&buf,&filesize,allocsizep); - return((uint8_t *)retptr); -} - -long komodo_stateind_validate(struct komodo_state *sp,char *indfname,uint8_t *filedata,long datalen,uint32_t *prevpos100p,uint32_t *indcounterp,char *symbol,char *dest) -{ - FILE *fp; long fsize,lastfpos=0,fpos=0; uint8_t *inds,func; int32_t i,n; uint32_t offset,tmp,prevpos100 = 0; - *indcounterp = *prevpos100p = 0; - if ( (inds= OS_fileptr(&fsize,indfname)) != 0 ) - { - lastfpos = 0; - fprintf(stderr,"inds.%p validate %s fsize.%ld datalen.%ld n.%d lastfpos.%ld\n",inds,indfname,fsize,datalen,(int32_t)(fsize / sizeof(uint32_t)),lastfpos); - if ( (fsize % sizeof(uint32_t)) == 0 ) - { - n = (int32_t)(fsize / sizeof(uint32_t)); - for (i=0; i n-10 ) - printf("%d: tmp.%08x [%c] prevpos100.%u\n",i,tmp,tmp&0xff,prevpos100); - if ( (i % 100) == 0 ) - prevpos100 = tmp; - else - { - func = (tmp & 0xff); - offset = (tmp >> 8); - fpos = prevpos100 + offset; - if ( lastfpos >= datalen || filedata[lastfpos] != func ) - { - printf("validate.%d error (%u %d) prev100 %u -> fpos.%ld datalen.%ld [%d] (%c) vs (%c) lastfpos.%ld\n",i,offset,func,prevpos100,fpos,datalen,lastfpos < datalen ? filedata[lastfpos] : -1,func,filedata[lastfpos],lastfpos); - return(-1); - } - } - lastfpos = fpos; - } - *indcounterp = n; - *prevpos100p = prevpos100; - if ( sp != 0 ) - komodo_stateind_set(sp,(uint32_t *)inds,n,filedata,fpos,symbol,dest); - //printf("free inds.%p %s validated[%d] fpos.%ld datalen.%ld, offset %ld vs fsize.%ld\n",inds,indfname,i,fpos,datalen,i * sizeof(uint32_t),fsize); - free(inds); - return(fpos); - } else printf("wrong filesize %s %ld\n",indfname,fsize); - } - free(inds); - fprintf(stderr,"indvalidate return -1\n"); - return(-1); -} - -long komodo_indfile_update(FILE *indfp,uint32_t *prevpos100p,long lastfpos,long newfpos,uint8_t func,uint32_t *indcounterp) -{ - uint32_t tmp; - if ( indfp != 0 ) - { - tmp = ((uint32_t)(newfpos - *prevpos100p) << 8) | (func & 0xff); - if ( ftell(indfp)/sizeof(uint32_t) != *indcounterp ) - printf("indfp fpos %ld -> ind.%ld vs counter.%u\n",ftell(indfp),ftell(indfp)/sizeof(uint32_t),*indcounterp); - //fprintf(stderr,"ftell.%ld indcounter.%u lastfpos.%ld newfpos.%ld func.%02x\n",ftell(indfp),*indcounterp,lastfpos,newfpos,func); - fwrite(&tmp,1,sizeof(tmp),indfp), (*indcounterp)++; - if ( (*indcounterp % 100) == 0 ) - { - *prevpos100p = (uint32_t)newfpos; - fwrite(prevpos100p,1,sizeof(*prevpos100p),indfp), (*indcounterp)++; - } - } - return(newfpos); -} - -int32_t komodo_faststateinit(struct komodo_state *sp,char *fname,char *symbol,char *dest) -{ - FILE *indfp; char indfname[1024]; uint8_t *filedata; long validated=-1,datalen,fpos,lastfpos; uint32_t tmp,prevpos100,indcounter,starttime; int32_t func,finished = 0; - starttime = (uint32_t)time(NULL); - safecopy(indfname,fname,sizeof(indfname)-4); - strcat(indfname,".ind"); - if ( (filedata= OS_fileptr(&datalen,fname)) != 0 ) - { - if ( 1 )//datalen >= (1LL << 32) || GetArg("-genind",0) != 0 || (validated= komodo_stateind_validate(0,indfname,filedata,datalen,&prevpos100,&indcounter,symbol,dest)) < 0 ) - { - lastfpos = fpos = 0; - indcounter = prevpos100 = 0; - if ( (indfp= fopen(indfname,"wb")) != 0 ) - fwrite(&prevpos100,1,sizeof(prevpos100),indfp), indcounter++; - fprintf(stderr,"processing %s %ldKB, validated.%ld\n",fname,datalen/1024,validated); - while ( (func= komodo_parsestatefiledata(sp,filedata,&fpos,datalen,symbol,dest)) >= 0 ) - { - lastfpos = komodo_indfile_update(indfp,&prevpos100,lastfpos,fpos,func,&indcounter); - } - if ( indfp != 0 ) - { - fclose(indfp); - if ( (fpos= komodo_stateind_validate(0,indfname,filedata,datalen,&prevpos100,&indcounter,symbol,dest)) < 0 ) - printf("unexpected komodostate.ind validate failure %s datalen.%ld\n",indfname,datalen); - else printf("%s validated fpos.%ld\n",indfname,fpos); - } - finished = 1; - fprintf(stderr,"took %d seconds to process %s %ldKB\n",(int32_t)(time(NULL)-starttime),fname,datalen/1024); - } - else if ( validated > 0 ) - { - if ( (indfp= fopen(indfname,"rb+")) != 0 ) - { - lastfpos = fpos = validated; - fprintf(stderr,"datalen.%ld validated %ld -> indcounter %u, prevpos100 %u offset.%d\n",datalen,validated,indcounter,prevpos100,(int32_t)(indcounter * sizeof(uint32_t))); - if ( fpos < datalen ) - { - fseek(indfp,indcounter * sizeof(uint32_t),SEEK_SET); - if ( ftell(indfp) == indcounter * sizeof(uint32_t) ) - { - while ( (func= komodo_parsestatefiledata(sp,filedata,&fpos,datalen,symbol,dest)) >= 0 ) - { - lastfpos = komodo_indfile_update(indfp,&prevpos100,lastfpos,fpos,func,&indcounter); - if ( lastfpos != fpos ) - fprintf(stderr,"unexpected lastfpos.%ld != %ld\n",lastfpos,fpos); - } - } - fclose(indfp); - } - if ( komodo_stateind_validate(sp,indfname,filedata,datalen,&prevpos100,&indcounter,symbol,dest) < 0 ) - printf("unexpected komodostate.ind validate failure %s datalen.%ld\n",indfname,datalen); - else - { - printf("%s validated updated from validated.%ld to %ld new.[%ld] -> indcounter %u, prevpos100 %u offset.%ld | elapsed %d seconds\n",indfname,validated,fpos,fpos-validated,indcounter,prevpos100,indcounter * sizeof(uint32_t),(int32_t)(time(NULL) - starttime)); - finished = 1; - } - } - } else printf("komodo_faststateinit unexpected case\n"); - free(filedata); - return(finished == 1); - } - return(-1); -} - -uint64_t komodo_interestsum(); - -void komodo_passport_iteration() -{ - static long lastpos[34]; static char userpass[33][1024]; static uint32_t lasttime,callcounter,lastinterest; - int32_t maxseconds = 10; - FILE *fp; uint8_t *filedata; long fpos,datalen,lastfpos; int32_t baseid,limit,n,ht,isrealtime,expired,refid,blocks,longest; struct komodo_state *sp,*refsp; char *retstr,fname[512],*base,symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; uint32_t buf[3],starttime; uint64_t RTmask = 0; //CBlockIndex *pindex; - expired = 0; - while ( 0 && KOMODO_INITDONE == 0 ) - { - fprintf(stderr,"[%s] PASSPORT iteration waiting for KOMODO_INITDONE\n",ASSETCHAINS_SYMBOL); - sleep(3); - } - if ( komodo_chainactive_timestamp() > lastinterest ) - { - if ( ASSETCHAINS_SYMBOL[0] == 0 ) - komodo_interestsum(); - //komodo_longestchain(); - lastinterest = komodo_chainactive_timestamp(); - } - refsp = komodo_stateptr(symbol,dest); - if ( ASSETCHAINS_SYMBOL[0] == 0 || strcmp(ASSETCHAINS_SYMBOL,"KMDCC") == 0 ) - { - refid = 33; - limit = 10000000; - jumblr_iteration(); - } - else - { - limit = 10000000; - refid = komodo_baseid(ASSETCHAINS_SYMBOL)+1; // illegal base -> baseid.-1 -> 0 - if ( refid == 0 ) - { - KOMODO_PASSPORT_INITDONE = 1; - return; - } - } - /*if ( KOMODO_PAX == 0 ) - { - KOMODO_PASSPORT_INITDONE = 1; - return; - }*/ - starttime = (uint32_t)time(NULL); - if ( callcounter++ < 1 ) - limit = 10000; - lasttime = starttime; - for (baseid=32; baseid>=0; baseid--) - { - if ( time(NULL) >= starttime+maxseconds ) - break; - sp = 0; - isrealtime = 0; - base = (char *)CURRENCIES[baseid]; - //printf("PASSPORT %s baseid+1 %d refid.%d\n",ASSETCHAINS_SYMBOL,baseid+1,refid); - if ( baseid+1 != refid ) // only need to import state from a different coin - { - if ( baseid == 32 ) // only care about KMD's state - { - refsp->RTmask &= ~(1LL << baseid); - komodo_statefname(fname,baseid<32?base:(char *)"",(char *)"komodostate"); - komodo_nameset(symbol,dest,base); - sp = komodo_stateptrget(symbol); - n = 0; - if ( lastpos[baseid] == 0 && (filedata= OS_fileptr(&datalen,fname)) != 0 ) - { - fpos = 0; - fprintf(stderr,"%s processing %s %ldKB\n",ASSETCHAINS_SYMBOL,fname,datalen/1024); - while ( komodo_parsestatefiledata(sp,filedata,&fpos,datalen,symbol,dest) >= 0 ) - lastfpos = fpos; - fprintf(stderr,"%s took %d seconds to process %s %ldKB\n",ASSETCHAINS_SYMBOL,(int32_t)(time(NULL)-starttime),fname,datalen/1024); - lastpos[baseid] = lastfpos; - free(filedata), filedata = 0; - datalen = 0; - } - else if ( (fp= fopen(fname,"rb")) != 0 && sp != 0 ) - { - fseek(fp,0,SEEK_END); - //fprintf(stderr,"couldnt OS_fileptr(%s), freading %ldKB\n",fname,ftell(fp)/1024); - if ( ftell(fp) > lastpos[baseid] ) - { - if ( ASSETCHAINS_SYMBOL[0] != 0 ) - printf("%s passport refid.%d %s fname.(%s) base.%s %ld %ld\n",ASSETCHAINS_SYMBOL,refid,symbol,fname,base,ftell(fp),lastpos[baseid]); - fseek(fp,lastpos[baseid],SEEK_SET); - while ( komodo_parsestatefile(sp,fp,symbol,dest) >= 0 && n < limit ) - { - if ( n == limit-1 ) - { - if ( time(NULL) < starttime+maxseconds ) - n = 0; - else - { - //printf("expire passport loop %s -> %s at %ld\n",ASSETCHAINS_SYMBOL,base,lastpos[baseid]); - expired++; - } - } - n++; - } - lastpos[baseid] = ftell(fp); - if ( 0 && lastpos[baseid] == 0 && strcmp(symbol,"KMD") == 0 ) - printf("from.(%s) lastpos[%s] %ld isrt.%d\n",ASSETCHAINS_SYMBOL,CURRENCIES[baseid],lastpos[baseid],komodo_isrealtime(&ht)); - } //else fprintf(stderr,"%s.%ld ",CURRENCIES[baseid],ftell(fp)); - fclose(fp); - } else fprintf(stderr,"load error.(%s) %p\n",fname,sp); - komodo_statefname(fname,baseid<32?base:(char *)"",(char *)"realtime"); - if ( (fp= fopen(fname,"rb")) != 0 ) - { - if ( fread(buf,1,sizeof(buf),fp) == sizeof(buf) ) - { - sp->CURRENT_HEIGHT = buf[0]; - if ( buf[0] != 0 && buf[0] >= buf[1] && buf[2] > time(NULL)-60 ) - { - isrealtime = 1; - RTmask |= (1LL << baseid); - memcpy(refsp->RTbufs[baseid+1],buf,sizeof(refsp->RTbufs[baseid+1])); - } - else if ( KOMODO_PAX != 0 && (time(NULL)-buf[2]) > 60 && ASSETCHAINS_SYMBOL[0] != 0 ) - fprintf(stderr,"[%s]: %s not RT %u %u %d\n",ASSETCHAINS_SYMBOL,base,buf[0],buf[1],(int32_t)(time(NULL)-buf[2])); - } //else fprintf(stderr,"%s size error RT\n",base); - fclose(fp); - } //else fprintf(stderr,"%s open error RT\n",base); - } - } - else - { - refsp->RTmask &= ~(1LL << baseid); - komodo_statefname(fname,baseid<32?base:(char *)"",(char *)"realtime"); - if ( (fp= fopen(fname,"wb")) != 0 ) - { - buf[0] = (uint32_t)chainActive.LastTip()->GetHeight(); - buf[1] = (uint32_t)komodo_longestchain(); - if ( buf[0] != 0 && buf[0] == buf[1] ) - { - buf[2] = (uint32_t)time(NULL); - RTmask |= (1LL << baseid); - memcpy(refsp->RTbufs[baseid+1],buf,sizeof(refsp->RTbufs[baseid+1])); - if ( refid != 0 ) - memcpy(refsp->RTbufs[0],buf,sizeof(refsp->RTbufs[0])); - } - if ( fwrite(buf,1,sizeof(buf),fp) != sizeof(buf) ) - fprintf(stderr,"[%s] %s error writing realtime\n",ASSETCHAINS_SYMBOL,base); - fclose(fp); - } else fprintf(stderr,"%s create error RT\n",base); - } - if ( sp != 0 && isrealtime == 0 ) - refsp->RTbufs[0][2] = 0; - } - //komodo_paxtotal(); // calls komodo_isrealtime(), which calls komodo_longestchain() - refsp->RTmask |= RTmask; - if ( expired == 0 && KOMODO_PASSPORT_INITDONE == 0 ) - { - KOMODO_PASSPORT_INITDONE = 1; - printf("READY for %s RPC calls at %u! done PASSPORT %s refid.%d\n",ASSETCHAINS_SYMBOL,(uint32_t)time(NULL),ASSETCHAINS_SYMBOL,refid); - } -} - - -extern std::vector Mineropret; // opreturn data set by the data gathering code -#define PRICES_ERRORRATE (COIN / 100) // maximum acceptable change, set at 1% -#define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) // 4 uint32_t unixtimestamp, BTCUSD, BTCGBP and BTCEUR -#define KOMODO_LOCALPRICE_CACHESIZE 13 -#define KOMODO_MAXPRICES 2048 -#define PRICES_SMOOTHWIDTH 1 - -#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) - -const char *Cryptos[] = { "KMD", "ETH" }; // must be on binance (for now) -// "LTC", "BCHABC", "XMR", "IOTA", "ZEC", "WAVES", "LSK", "DCR", "RVN", "DASH", "XEM", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" -const char *Forex[] = -{ "BGN","NZD","ILS","RUB","CAD","PHP","CHF","AUD","JPY","TRY","HKD","MYR","HRK","CZK","IDR","DKK","NOK","HUF","GBP","MXN","THB","ISK","ZAR","BRL","SGD","PLN","INR","KRW","RON","CNY","SEK","EUR" -}; // must be in ECB list - -struct komodo_extremeprice -{ - uint256 blockhash; - uint32_t pricebits,timestamp; - int32_t height; - int16_t dir,ind; -} ExtremePrice; - -struct komodo_priceinfo -{ - FILE *fp; - char symbol[64]; -} PRICES[KOMODO_MAXPRICES]; - -uint32_t PriceCache[KOMODO_LOCALPRICE_CACHESIZE][KOMODO_MAXPRICES];//4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex)]; -int64_t PriceMult[KOMODO_MAXPRICES]; -int32_t komodo_cbopretsize(uint64_t flags); - -void komodo_PriceCache_shift() -{ - int32_t i; - for (i=KOMODO_LOCALPRICE_CACHESIZE-1; i>0; i--) - memcpy(PriceCache[i],PriceCache[i-1],sizeof(PriceCache[i])); - memcpy(PriceCache[0],Mineropret.data(),Mineropret.size()); -} - -int32_t _komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,CBlock *block) -{ - CTransaction tx; int32_t numvouts; std::vector vopret; - tx = block->vtx[0]; - numvouts = (int32_t)tx.vout.size(); - GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); - if ( vopret.size() >= PRICES_SIZEBIT0 ) - { - if ( seedp != 0 ) - memcpy(seedp,&block->hashMerkleRoot,sizeof(*seedp)); - memcpy(heightbits,vopret.data(),vopret.size()); - return((int32_t)(vopret.size()/sizeof(uint32_t))); - } - return(-1); -} - -// komodo_heightpricebits() extracts the price data in the coinbase for nHeight -int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight) -{ - CBlockIndex *pindex; CBlock block; - if ( seedp != 0 ) - *seedp = 0; - if ( (pindex= komodo_chainactive(nHeight)) != 0 ) - { - if ( komodo_blockload(block,pindex) == 0 ) - { - return(_komodo_heightpricebits(seedp,heightbits,&block)); - } - } - fprintf(stderr,"couldnt get pricebits for %d\n",nHeight); - return(-1); -} - -/* - komodo_pricenew() is passed in a reference price, the change tolerance and the proposed price. it needs to return a clipped price if it is too big and also set a flag if it is at or above the limit - */ -uint32_t komodo_pricenew(char *maxflagp,uint32_t price,uint32_t refprice,int64_t tolerance) -{ - uint64_t highprice,lowprice; - if ( refprice < 2 ) - return(0); - highprice = ((uint64_t)refprice * (COIN + tolerance)) / COIN; // calc highest acceptable price - lowprice = ((uint64_t)refprice * (COIN - tolerance)) / COIN; // and lowest - if ( highprice == refprice ) - highprice++; - if ( lowprice == refprice ) - lowprice--; - if ( price >= highprice ) - { - //fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); - if ( price > highprice ) // return non-zero only if we violate the tolerance - { - *maxflagp = 2; - return(highprice); - } - *maxflagp = 1; - } - else if ( price <= lowprice ) - { - //fprintf(stderr,"low %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); - if ( price < lowprice ) - { - *maxflagp = -2; - return(lowprice); - } - *maxflagp = -1; - } - return(0); -} - -// komodo_pricecmp() returns -1 if any of the prices are beyond the tolerance -int32_t komodo_pricecmp(int32_t nHeight,int32_t n,char *maxflags,uint32_t *pricebitsA,uint32_t *pricebitsB,int64_t tolerance) -{ - int32_t i; uint32_t newprice; - for (i=1; i newprice.%u out of tolerance maxflag.%d\n",nHeight,i,n,pricebitsB[i],pricebitsA[i],newprice,maxflags[i]); - return(-1); - } - } - return(0); -} - -// komodo_priceclamp() clamps any price that is beyond tolerance -int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int64_t tolerance) -{ - int32_t i; uint32_t newprice; char maxflags[KOMODO_MAXPRICES]; - memset(maxflags,0,sizeof(maxflags)); - for (i=1; i %u\n",i,n,refprices[i],pricebits[i],newprice); - pricebits[i] = newprice; - } - } - return(0); -} - -// komodo_mineropret() returns a valid pricedata to add to the coinbase opreturn for nHeight -CScript komodo_mineropret(int32_t nHeight) -{ - CScript opret; char maxflags[KOMODO_MAXPRICES]; uint32_t pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES]; int32_t maxflag,i,n,numzero=0; - if ( Mineropret.size() >= PRICES_SIZEBIT0 ) - { - n = (int32_t)(Mineropret.size() / sizeof(uint32_t)); - numzero = 1; - while ( numzero > 0 ) - { - memcpy(pricebits,Mineropret.data(),Mineropret.size()); - for (i=numzero=0; i 0 ) - { - memcpy(pricebits,Mineropret.data(),Mineropret.size()); - memset(maxflags,0,sizeof(maxflags)); - if ( komodo_pricecmp(0,n,maxflags,pricebits,prevbits,PRICES_ERRORRATE) < 0 ) - { - // if the new prices are outside tolerance, update Mineropret with clamped prices - komodo_priceclamp(n,pricebits,prevbits,PRICES_ERRORRATE); - //fprintf(stderr,"update Mineropret to clamped prices\n"); - memcpy(Mineropret.data(),pricebits,Mineropret.size()); - } - } - int32_t i; - for (i=0; i vopret; char maxflags[KOMODO_MAXPRICES]; uint256 bhash; double btcusd,btcgbp,btceur; uint32_t localbits[KOMODO_MAXPRICES],pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now; - now = (uint32_t)time(NULL); - if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) - { - bhash = block->GetHash(); - GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() >= PRICES_SIZEBIT0 ) - { - n = (int32_t)(vopret.size() / sizeof(uint32_t)); - memcpy(pricebits,vopret.data(),Mineropret.size()); - memset(maxflags,0,sizeof(maxflags)); - if ( nHeight > 2 ) - { - prevtime = previndex->nTime; - lag = (int32_t)(now - pricebits[0]); - lag2 = (int32_t)(pricebits[0] - prevtime); - lag3 = (int32_t)(block->nTime - pricebits[0]); - if ( lag < -60 ) // avoid data from future - { - fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); - return(-1); - } - if ( lag2 < -60 ) //testchain_exemption ) // must be close to last block timestamp - { - fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); - if ( nHeight > testchain_exemption ) - return(-1); - } - if ( lag3 < -60 || lag3 > ASSETCHAINS_BLOCKTIME ) - { - fprintf(stderr,"C ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); - if ( nHeight > testchain_exemption ) - return(-1); - } - btcusd = (double)pricebits[1]/10000; - btcgbp = (double)pricebits[2]/10000; - btceur = (double)pricebits[3]/10000; - fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); - if ( komodo_heightpricebits(0,prevbits,nHeight-1) > 0 ) - { - if ( nHeight < testchain_exemption ) - { - for (i=0; i= PRICES_SIZEBIT0 ) - { - memcpy(localbits,Mineropret.data(),Mineropret.size()); - if ( nHeight < testchain_exemption ) - { - for (i=0; i 0 && localbits[i] < prevbits[i] ) - { - if ( iter == 0 ) - break; - // second iteration checks recent prices to see if within local volatility - for (j=0; j= prevbits[i] ) - { - fprintf(stderr,"i.%d within recent localprices[%d] %u >= %u\n",i,j,PriceCache[j][i],prevbits[i]); - break; - } - if ( j == KOMODO_LOCALPRICE_CACHESIZE ) - { - komodo_queuelocalprice(1,nHeight,block->nTime,bhash,i,prevbits[i]); - break; - } - } - else if ( maxflag < 0 && localbits[i] > prevbits[i] ) - { - if ( iter == 0 ) - break; - for (j=0; jnTime,bhash,i,prevbits[i]); - break; - } - } - } - } - if ( i != n ) - { - if ( iter == 0 ) - { - fprintf(stderr,"force update prices\n"); - komodo_cbopretupdate(1); - memcpy(localbits,Mineropret.data(),Mineropret.size()); - } else return(-1); - } - } - } - } - if ( bhash == ExtremePrice.blockhash ) - { - fprintf(stderr,"approved a previously extreme price based on new data ht.%d vs %u vs %u\n",ExtremePrice.height,ExtremePrice.timestamp,(uint32_t)block->nTime); - memset(&ExtremePrice,0,sizeof(ExtremePrice)); - } - return(0); - } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)Mineropret.size(),(int32_t)scriptPubKey.size(),scriptPubKey[0]); - return(-1); - } - return(0); -} - -char *nonportable_path(char *str) -{ - int32_t i; - for (i=0; str[i]!=0; i++) - if ( str[i] == '/' ) - str[i] = '\\'; - return(str); -} - -char *portable_path(char *str) -{ -#ifdef _WIN32 - return(nonportable_path(str)); -#else -#ifdef __PNACL - /*int32_t i,n; - if ( str[0] == '/' ) - return(str); - else - { - n = (int32_t)strlen(str); - for (i=n; i>0; i--) - str[i] = str[i-1]; - str[0] = '/'; - str[n+1] = 0; - }*/ -#endif - return(str); -#endif -} - -void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) -{ - FILE *fp; - long filesize,buflen = *allocsizep; - uint8_t *buf = *bufp; - *lenp = 0; - if ( (fp= fopen(portable_path(fname),"rb")) != 0 ) - { - fseek(fp,0,SEEK_END); - filesize = ftell(fp); - if ( filesize == 0 ) - { - fclose(fp); - *lenp = 0; - //printf("loadfile null size.(%s)\n",fname); - return(0); - } - if ( filesize > buflen ) - { - *allocsizep = filesize; - *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); - } - rewind(fp); - if ( buf == 0 ) - printf("Null buf ???\n"); - else - { - if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) - printf("error reading filesize.%ld\n",(long)filesize); - buf[filesize] = 0; - } - fclose(fp); - *lenp = filesize; - //printf("loaded.(%s)\n",buf); - } //else printf("OS_loadfile couldnt load.(%s)\n",fname); - return(buf); -} - -void *filestr(long *allocsizep,char *_fname) -{ - long filesize = 0; char *fname,*buf = 0; void *retptr; - *allocsizep = 0; - fname = (char *)malloc(strlen(_fname)+1); - strcpy(fname,_fname); - retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); - free(fname); - return(retptr); -} - -cJSON *send_curl(char *url,char *fname) -{ - long fsize; char curlstr[1024],*jsonstr; cJSON *json=0; - sprintf(curlstr,"wget -q \"%s\" -O %s",url,fname); - if ( system(curlstr) == 0 ) - { - if ( (jsonstr= (char *)filestr((long *)&fsize,fname)) != 0 ) - { - json = cJSON_Parse(jsonstr); - free(jsonstr); - } - } - return(json); -} - -// get_urljson just returns the JSON returned by the URL using issue_curl - - -/* -const char *Techstocks[] = -{ "AAPL","ADBE","ADSK","AKAM","AMD","AMZN","ATVI","BB","CDW","CRM","CSCO","CYBR","DBX","EA","FB","GDDY","GOOG","GRMN","GSAT","HPQ","IBM","INFY","INTC","INTU","JNPR","MSFT","MSI","MU","MXL","NATI","NCR","NFLX","NTAP","NVDA","ORCL","PANW","PYPL","QCOM","RHT","S","SHOP","SNAP","SPOT","SYMC","SYNA","T","TRIP","TWTR","TXN","VMW","VOD","VRSN","VZ","WDC","XRX","YELP","YNDX","ZEN" -}; -const char *Metals[] = { "XAU", "XAG", "XPT", "XPD", }; - -const char *Markets[] = { "DJIA", "SPX", "NDX", "VIX" }; -*/ - -cJSON *get_urljson(char *url) -{ - char *jsonstr; cJSON *json = 0; - if ( (jsonstr= issue_curl(url)) != 0 ) - { - //fprintf(stderr,"(%s) -> (%s)\n",url,jsonstr); - json = cJSON_Parse(jsonstr); - free(jsonstr); - } - return(json); -} - -int32_t get_stockprices(uint32_t now,uint32_t *prices,std::vector symbols) -{ - char url[32768],*symbol,*timestr; cJSON *json,*obj; int32_t i,n=0,retval=-1; uint32_t uprice,timestamp; - sprintf(url,"https://api.iextrading.com/1.0/tops/last?symbols=%s",GetArg("-ac_stocks","").c_str()); - fprintf(stderr,"url.(%s)\n",url); - if ( (json= get_urljson(url)) != 0 ) //if ( (json= send_curl(url,(char *)"iex")) != 0 ) // - { - fprintf(stderr,"stocks.(%s)\n",jprint(json,0)); - if ( (n= cJSON_GetArraySize(json)) > 0 ) - { - retval = n; - for (i=0; i now+60 || timestamp < now-ASSETCHAINS_BLOCKTIME ) - { - fprintf(stderr,"time error.%d (%u vs %u)\n",timestamp-now,timestamp,now); - retval = -1; - }*/ - if ( symbols[i] != symbol ) - { - retval = -1; - fprintf(stderr,"MISMATCH."); - } - fprintf(stderr,"(%s %u) ",symbol,uprice); - } - } - fprintf(stderr,"numstocks.%d\n",n); - } - //https://api.iextrading.com/1.0/tops/last?symbols=AAPL -> [{"symbol":"AAPL","price":198.63,"size":100,"time":1555092606076}] - free_json(json); - } - return(retval); -} - -uint32_t get_dailyfx(uint32_t *prices) -{ - //{"base":"USD","rates":{"BGN":1.74344803,"NZD":1.471652701,"ILS":3.6329113924,"RUB":65.1997682296,"CAD":1.3430201462,"USD":1.0,"PHP":52.8641469068,"CHF":0.9970582992,"AUD":1.4129078267,"JPY":110.6792654662,"TRY":5.6523444464,"HKD":7.8499732573,"MYR":4.0824567659,"HRK":6.6232840078,"CZK":22.9862720628,"IDR":14267.4986628633,"DKK":6.6551078624,"NOK":8.6806917454,"HUF":285.131039401,"GBP":0.7626582278,"MXN":19.4183455161,"THB":31.8702085933,"ISK":122.5708682475,"ZAR":14.7033339276,"BRL":3.9750401141,"SGD":1.3573720806,"PLN":3.8286682118,"INR":69.33187734,"KRW":1139.1602781244,"RON":4.2423783206,"CNY":6.7387234801,"SEK":9.3385630237,"EUR":0.8914244963},"date":"2019-03-28"} - char url[512],*datestr; cJSON *json,*rates; int32_t i; uint32_t datenum=0,price = 0; - sprintf(url,"https://api.openrates.io/latest?base=USD"); - if ( (json= get_urljson(url)) != 0 ) //if ( (json= send_curl(url,(char *)"dailyfx")) != 0 ) - { - if ( (rates= jobj(json,(char *)"rates")) != 0 ) - { - for (i=0; i strvec) -{ - int32_t i,errs=0; uint32_t price; char *symbol; - for (i=0; i 333 ) // for debug only! -// ASSETCHAINS_CBOPRET = 7; - size = komodo_cbopretsize(ASSETCHAINS_CBOPRET); - if ( Mineropret.size() < size ) - Mineropret.resize(size); - size = PRICES_SIZEBIT0; - if ( (forceflag != 0 || now > lastbtc+120) && get_btcusd(pricebits) == 0 ) - { - if ( flags == 0 ) - komodo_PriceCache_shift(); - memcpy(PriceCache[0],pricebits,PRICES_SIZEBIT0); - flags |= 1; - } - if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) - { - if ( now > lasttime+3600*5 || forexprices[0] == 0 ) // cant assume timestamp is valid for forex price as it is a daily weekday changing thing anyway. - { - get_dailyfx(forexprices); - if ( flags == 0 ) - komodo_PriceCache_shift(); - flags |= 2; - memcpy(&PriceCache[0][size/sizeof(uint32_t)],forexprices,sizeof(forexprices)); - } - size += (sizeof(Forex)/sizeof(*Forex)) * sizeof(uint32_t); - } - if ( (ASSETCHAINS_CBOPRET & 4) != 0 ) - { - if ( forceflag != 0 || flags != 0 ) - { - get_cryptoprices(pricebuf,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos)),ASSETCHAINS_PRICES); - if ( flags == 0 ) - komodo_PriceCache_shift(); - memcpy(&PriceCache[0][size/sizeof(uint32_t)],pricebuf,(sizeof(Cryptos)/sizeof(*Cryptos)+ASSETCHAINS_PRICES.size()) * sizeof(uint32_t)); - flags |= 4; // very rarely we can see flags == 6 case - } - size += (sizeof(Cryptos)/sizeof(*Cryptos)+ASSETCHAINS_PRICES.size()) * sizeof(uint32_t); - } - now = (uint32_t)time(NULL); - if ( (ASSETCHAINS_CBOPRET & 8) != 0 ) - { - if ( forceflag != 0 || flags != 0 ) - { - if ( get_stockprices(now,pricebuf,ASSETCHAINS_STOCKS) == ASSETCHAINS_STOCKS.size() ) - { - if ( flags == 0 ) - komodo_PriceCache_shift(); - memcpy(&PriceCache[0][size/sizeof(uint32_t)],pricebuf,ASSETCHAINS_STOCKS.size() * sizeof(uint32_t)); - flags |= 8; // very rarely we can see flags == 10 case - } - } - size += (ASSETCHAINS_STOCKS.size()) * sizeof(uint32_t); - } - if ( flags != 0 ) - { - if ( (flags & 1) != 0 ) - lastbtc = now; - if ( (flags & 2) != 0 ) - lasttime = now; - memcpy(Mineropret.data(),PriceCache[0],size); - if ( ExtremePrice.dir != 0 && ExtremePrice.ind > 0 && ExtremePrice.ind < size/sizeof(uint32_t) && now < ExtremePrice.timestamp+3600 ) - { - fprintf(stderr,"cmp dir.%d PriceCache[0][ExtremePrice.ind] %u >= %u ExtremePrice.pricebits\n",ExtremePrice.dir,PriceCache[0][ExtremePrice.ind],ExtremePrice.pricebits); - if ( (ExtremePrice.dir > 0 && PriceCache[0][ExtremePrice.ind] >= ExtremePrice.pricebits) || (ExtremePrice.dir < 0 && PriceCache[0][ExtremePrice.ind] <= ExtremePrice.pricebits) ) - { - fprintf(stderr,"future price is close enough to allow approving previously rejected block ind.%d %u vs %u\n",ExtremePrice.ind,PriceCache[0][ExtremePrice.ind],ExtremePrice.pricebits); - if ( (pindex= komodo_blockindex(ExtremePrice.blockhash)) != 0 ) - pindex->nStatus &= ~BLOCK_FAILED_MASK; - else fprintf(stderr,"couldnt find block.%s\n",ExtremePrice.blockhash.GetHex().c_str()); - } - } - // high volatility still strands nodes so we need to check new prices to approve a stuck block - // scan list of stuck blocks (one?) and auto reconsiderblock if it changed state - - //int32_t i; for (i=0; i= KOMODO_MAXPRICES ) - return(-1); - mult = komodo_pricemult(ind); - if ( nonzprices != 0 ) - memset(nonzprices,0,sizeof(*nonzprices)*PRICES_DAYWINDOW); - //for (i=0; i= PRICES_DAYWINDOW ) - i = 0; - if ( (price= rawprices[i*rawskip]) == 0 ) - { - fprintf(stderr,"null rawprice.[%d]\n",i); - return(-1); - } - if ( price >= lowprice && price <= highprice ) - { - //fprintf(stderr,"%.1f ",(double)price/10000); - sum += price; - correlation++; - if ( correlation > (PRICES_DAYWINDOW>>1) ) - { - if ( nonzprices == 0 ) - return(refprice * mult); - //fprintf(stderr,"-> %.4f\n",(double)sum*mult/correlation); - //return(sum*mult/correlation); - n = 0; - i = (iter + seed) % PRICES_DAYWINDOW; - for (k=0; k= PRICES_DAYWINDOW ) - i = 0; - if ( n > (PRICES_DAYWINDOW>>1) ) - nonzprices[i] = 0; - else - { - price = rawprices[i*rawskip]; - if ( price < lowprice || price > highprice ) - nonzprices[i] = 0; - else - { - nonzprices[i] = price; - //fprintf(stderr,"(%d %u) ",i,rawprices[i*rawskip]); - n++; - } - } - } - //fprintf(stderr,"ind.%d iter.%d j.%d i.%d n.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,n,correlation,(long long)refprice,(long long)sum/correlation); - if ( n != correlation ) - return(-1); - sum = den = n = 0; - for (i=0; i %.8f\n",(long long)firstprice,((double)(sum*mult) / den) / COIN); - return((sum * mult) / den); - } - } - } - if ( correlation > maxcorrelation ) - maxcorrelation = correlation; - } - fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d ref.%llu high.%llu low.%llu\n",ind,iter,maxcorrelation,(long long)refprice,(long long)highprice,(long long)lowprice); - return(0); -} - -int64_t _pairave64(int64_t valA,int64_t valB) -{ - if ( valA != 0 && valB != 0 ) - return((valA + valB) / 2); - else if ( valA != 0 ) return(valA); - else return(valB); -} - -int64_t _pairdiff64(register int64_t valA,register int64_t valB) -{ - if ( valA != 0 && valB != 0 ) - return(valA - valB); - else return(0); -} - -int64_t balanced_ave64(int64_t buf[],int32_t i,int32_t width) -{ - register int32_t nonz,j; register int64_t sum,price; - nonz = 0; - sum = 0; - for (j=-width; j<=width; j++) - { - price = buf[i + j]; - if ( price != 0 ) - { - sum += price; - nonz++; - } - } - if ( nonz != 0 ) - sum /= nonz; - return(sum); -} - -void buf_trioave64(int64_t dest[],int64_t src[],int32_t n) -{ - register int32_t i,j,width = 3; - for (i=0; i<128; i++) - src[i] = 0; - //for (i=n-width-1; i>width; i--) - // dest[i] = balanced_ave(src,i,width); - //for (i=width; i>0; i--) - // dest[i] = balanced_ave(src,i,i); - for (i=1; i *(int64_t *)b) return 1; - else if ( (uint64_t)a < (uint64_t)b ) // jl777 prevent nondeterminism - return(-1); - else return(1); -} - -static void sort64(int64_t *l, int32_t llen) -{ - qsort(l,llen,sizeof(uint64_t),cmp_llu); -} - -static int revcmp_llu(const void *a, const void*b) -{ - if(*(int64_t *)a < *(int64_t *)b) return 1; - else if(*(int64_t *)a > *(int64_t *)b) return -1; - else if ( (uint64_t)a < (uint64_t)b ) // jl777 prevent nondeterminism - return(-1); - else return(1); -} - -static void revsort64(int64_t *l, int32_t llen) -{ - qsort(l,llen,sizeof(uint64_t),revcmp_llu); -} - -int64_t komodo_priceave(int64_t *buf,int64_t *correlated,int32_t cskip) -{ - int32_t i,dir=0; int64_t sum=0,nonzprice,price,halfave,thirdave,fourthave,decayprice; - if ( PRICES_DAYWINDOW < 2 ) - return(0); - for (i=0; i price ) // rising prices - sort64(buf,PRICES_DAYWINDOW); - else revsort64(buf,PRICES_DAYWINDOW); - decayprice = buf[0]; - for (i=0; i %.8f\n",halfave 0 && PRICES[0].fp != 0 && createflag != 0 ) - { - fseek(PRICES[0].fp,(2*PRICES_DAYWINDOW+PRICES_SMOOTHWIDTH) * sizeof(uint32_t) * i,SEEK_SET); - fputc(0,PRICES[0].fp); - fflush(PRICES[0].fp); - } - fprintf(stderr,"pricesinit done i.%d num.%d numprices.%d\n",i,num,(int32_t)(komodo_cbopretsize(ASSETCHAINS_CBOPRET)/sizeof(uint32_t))); - if ( i != num || i != komodo_cbopretsize(ASSETCHAINS_CBOPRET)/sizeof(uint32_t) ) - { - fprintf(stderr,"fatal error opening prices files, start shutdown\n"); - StartShutdown(); - } - return(0); -} - -pthread_mutex_t pricemutex; - -// PRICES file layouts -// [0] rawprice32 / timestamp -// [1] correlated -// [2] 24hr ave -// [3] to [7] reserved - -void komodo_pricesupdate(int32_t height,CBlock *pblock) -{ - static int numprices; static uint32_t *ptr32; static int64_t *ptr64,*tmpbuf; - int32_t ind,offset,width; int64_t correlated,smoothed; uint64_t seed,rngval; uint32_t rawprices[KOMODO_MAXPRICES],buf[PRICES_MAXDATAPOINTS*2]; - width = PRICES_DAYWINDOW;//(2*PRICES_DAYWINDOW + PRICES_SMOOTHWIDTH); - if ( numprices == 0 ) - { - pthread_mutex_init(&pricemutex,0); - numprices = (int32_t)(komodo_cbopretsize(ASSETCHAINS_CBOPRET) / sizeof(uint32_t)); - ptr32 = (uint32_t *)calloc(sizeof(uint32_t),numprices * width); - ptr64 = (int64_t *)calloc(sizeof(int64_t),PRICES_DAYWINDOW*PRICES_MAXDATAPOINTS); - tmpbuf = (int64_t *)calloc(sizeof(int64_t),2*PRICES_DAYWINDOW); - fprintf(stderr,"prices update: numprices.%d %p %p\n",numprices,ptr32,ptr64); - } - if ( _komodo_heightpricebits(&seed,rawprices,pblock) == numprices ) - { - //for (ind=0; ind PRICES_DAYWINDOW ) - { - fseek(PRICES[0].fp,(height-width+1) * numprices * sizeof(uint32_t),SEEK_SET); - if ( fread(ptr32,sizeof(uint32_t),width*numprices,PRICES[0].fp) == width*numprices ) - { - rngval = seed; - for (ind=1; ind 0 ) - { - fseek(PRICES[ind].fp,height * sizeof(int64_t) * PRICES_MAXDATAPOINTS,SEEK_SET); - memset(buf,0,sizeof(buf)); - buf[0] = rawprices[ind]; - buf[1] = rawprices[0]; // timestamp - memcpy(&buf[2],&correlated,sizeof(correlated)); - if ( fwrite(buf,1,sizeof(buf),PRICES[ind].fp) != sizeof(buf) ) - fprintf(stderr,"error fwrite buf for ht.%d ind.%d\n",height,ind); - else if ( height > PRICES_DAYWINDOW*2 ) - { - fseek(PRICES[ind].fp,(height-PRICES_DAYWINDOW+1) * PRICES_MAXDATAPOINTS * sizeof(int64_t),SEEK_SET); - if ( fread(ptr64,sizeof(int64_t),PRICES_DAYWINDOW*PRICES_MAXDATAPOINTS,PRICES[ind].fp) == PRICES_DAYWINDOW*PRICES_MAXDATAPOINTS ) - { - if ( (smoothed= komodo_priceave(tmpbuf,&ptr64[(PRICES_DAYWINDOW-1)*PRICES_MAXDATAPOINTS+1],-PRICES_MAXDATAPOINTS)) > 0 ) - { - fseek(PRICES[ind].fp,(height * PRICES_MAXDATAPOINTS + 2) * sizeof(int64_t),SEEK_SET); - if ( fwrite(&smoothed,1,sizeof(smoothed),PRICES[ind].fp) != sizeof(smoothed) ) - fprintf(stderr,"error fwrite smoothed for ht.%d ind.%d\n",height,ind); - else fflush(PRICES[ind].fp); - } else fprintf(stderr,"error price_smoothed ht.%d ind.%d\n",height,ind); - } else fprintf(stderr,"error fread ptr64 for ht.%d ind.%d\n",height,ind); - } - } else fprintf(stderr,"error komodo_pricecorrelated for ht.%d ind.%d\n",height,ind); - } - fprintf(stderr,"height.%d\n",height); - } else fprintf(stderr,"error reading rawprices for ht.%d\n",height); - } else fprintf(stderr,"height.%d <= width.%d\n",height,width); - pthread_mutex_unlock(&pricemutex); - } else fprintf(stderr,"null PRICES[0].fp\n"); - } else fprintf(stderr,"numprices mismatch, height.%d\n",height); -} - -int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblocks) -{ - FILE *fp; int32_t retval = PRICES_MAXDATAPOINTS; - pthread_mutex_lock(&pricemutex); - if ( ind < KOMODO_MAXPRICES && (fp= PRICES[ind].fp) != 0 ) - { - fseek(fp,height * PRICES_MAXDATAPOINTS * sizeof(int64_t),SEEK_SET); - if ( fread(buf64,sizeof(int64_t),numblocks*PRICES_MAXDATAPOINTS,fp) != numblocks*PRICES_MAXDATAPOINTS ) - retval = -1; - } - pthread_mutex_unlock(&pricemutex); - return(retval); -} diff --git a/src/komodo_globals.h b/src/komodo_globals.h index e0d22e439cb..47af0d4f384 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -44,13 +44,16 @@ struct komodo_state KOMODO_STATES[34]; const uint32_t nStakedDecemberHardforkTimestamp = 1576840000; //December 2019 hardfork 12/20/2019 @ 11:06am (UTC) const int32_t nDecemberHardforkHeight = 1670000; //December 2019 hardfork +const uint32_t nS4Timestamp = 1592146800; //dPoW Season 4 2020 hardfork Sunday, June 14th, 2020 03:00:00 PM UTC +const int32_t nS4HardforkHeight = 1922000; //dPoW Season 4 2020 hardfork Sunday, June 14th, 2020 + #define _COINBASE_MATURITY 100 int COINBASE_MATURITY = _COINBASE_MATURITY;//100; unsigned int WITNESS_CACHE_SIZE = _COINBASE_MATURITY+10; uint256 KOMODO_EARLYTXID; int32_t KOMODO_MININGTHREADS = -1,IS_KOMODO_NOTARY,IS_STAKED_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND,STAKED_ERA,KOMODO_CONNECTING = -1,KOMODO_DEALERNODE,KOMODO_EXTRASATOSHI,ASSETCHAINS_FOUNDERS,ASSETCHAINS_CBMATURITY,KOMODO_NSPV; -int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,KOMODO_CCACTIVATE,KOMODO_DEX_P2P,JUMBLR_PAUSE = 1; +int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,KOMODO_CCACTIVATE,JUMBLR_PAUSE = 1; std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,NOTARY_ADDRESS,ASSETCHAINS_SELFIMPORT,ASSETCHAINS_CCLIB; uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEYHASH[20],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE,ASSETCHAINS_TXPOW,ASSETCHAINS_MARMARA; int8_t ASSETCHAINS_ADAPTIVEPOW; @@ -79,8 +82,8 @@ uint64_t ASSETCHAINS_TIMEUNLOCKFROM = 0, ASSETCHAINS_TIMEUNLOCKTO = 0,ASSETCHAIN uint64_t ASSETCHAINS_LASTERA = 1; uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS+1],ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS+1],ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS+1],ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS+1],ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS+1],ASSETCHAINS_PEGSCCPARAMS[3]; -uint8_t ASSETCHAINS_CCDISABLES[256],ASSETCHAINS_CCZEROTXFEE[256]; -//std::vector ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS; +uint8_t ASSETCHAINS_CCDISABLES[256]; +std::vector ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS; #define _ASSETCHAINS_EQUIHASH 0 uint32_t ASSETCHAINS_NUMALGOS = 3; diff --git a/src/komodo_nSPV_defs.h b/src/komodo_nSPV_defs.h index 0cf0c68cdde..40d9dc02229 100644 --- a/src/komodo_nSPV_defs.h +++ b/src/komodo_nSPV_defs.h @@ -59,7 +59,6 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int UniValue NSPV_spend(char *srcaddr,char *destaddr,int64_t satoshis); extern uint256 SIG_TXHASH; uint32_t NSPV_blocktime(int32_t hdrheight); -int32_t komodo_DEXprocess(CNode *pfrom,std::vector &response,uint8_t *msg,int32_t len); struct NSPV_equihdr { diff --git a/src/komodo_nSPV_fullnode.h b/src/komodo_nSPV_fullnode.h index 535b7d3048f..90ec9027cfd 100644 --- a/src/komodo_nSPV_fullnode.h +++ b/src/komodo_nSPV_fullnode.h @@ -22,26 +22,11 @@ #include "notarisationdb.h" #include "rpc/server.h" -static std::map nspv_remote_commands = { - -{"channelsopen", true},{"channelspayment", true},{"channelsclose", true},{"channelsrefund", true}, +static std::map nspv_remote_commands = {{"channelsopen", true},{"channelspayment", true},{"channelsclose", true},{"channelsrefund", true}, {"channelslist", true},{"channelsinfo", true},{"oraclescreate", true},{"oraclesfund", true},{"oraclesregister", true},{"oraclessubscribe", true}, -{"oraclesdata", true},{"oraclesinfo", false},{"oracleslist", false},{"gatewaysbind", true},{"gatewaysdeposit", true},{"gatewayswithdraw", true}, -{"gatewayswithdrawsign", true},{"gatewaysmarkdone", true},{"gatewayspendingdeposits", true},{"gatewayspendingsignwithdraws", true},{"gatewayssignedwithdraws", true}, -{"gatewaysinfo", false},{"gatewayslist", false},{"faucetfund", true},{"faucetget", true},{"pegscreate", true},{"pegsfund", true},{"pegsget", true},{"pegsclose", true}, -{"pegsclose", true},{"pegsredeem", true},{"pegsexchange", true},{"pegsliquidate", true},{"pegsaccounthistory", true},{"pegsaccountinfo", true},{"pegsworstaccounts", true}, -{"pegsinfo", true},{ "marmaralock", false },{ "marmaraissue", false },{ "marmaratransfer", true },{ "marmarareceive", true },{ "marmarainfo", true },{ "marmaracreditloop", true }, - // kogs: - { "kogskoglist", true }, { "kogscreategameconfig", true }, { "kogsstartgame", true }, { "kogscreateplayer", true }, { "kogscreatekogs", true }, { "kogscreateslammers", true }, - { "kogscreatepack", true }, { "kogsunsealpack", true }, { "kogscreatecontainer", true }, { "kogsdepositcontainer", true }, { "kogsaddkogstocontainer", true }, - { "kogsremovekogsfromcontainer", true }, { "kogsaddress", true }, { "kogsburntoken", true }, { "kogspacklist", true }, { "kogskoglist", true }, - { "kogsslammerlist", true }, { "kogscontainerlist", true }, { "kogsdepositedcontainerlist", true }, { "kogsplayerlist", true }, { "kogsgameconfiglist", true }, - { "kogsgamelist", true }, { "kogsremoveobject", true }, { "kogsslamdata", true }, { "kogsobjectinfo", true }, { "kogsadvertiseplayer", true },{ "kogsstopadvertiseplayer", true }, { "kogsadvertisedplayerlist", true }, - { "kogsbalance", true }, - // tokens: - { "tokenask", true }, { "tokenbid", true }, { "tokenfillask", true }, { "tokenfillbid", true }, { "tokencancelask", true }, { "tokencancelbid", true }, - { "tokenorders", true }, { "mytokenorders", true }, { "tokentransfer", true },{ "tokencreate", false } -}; +{"oraclesdata", true},{"oraclesinfo", false},{"oracleslist", false},{"gatewaysbind", true},{"gatewaysdeposit", true},{"gatewaysclaim", true},{"gatewayswithdraw", true}, +{"gatewayspartialsign", true},{"gatewayscompletesigning", true},{"gatewaysmarkdone", true},{"gatewayspendingdeposits", true},{"gatewayspendingwithdraws", true}, +{"gatewaysprocessed", true},{"gatewaysinfo", false},{"gatewayslist", false},{"faucetfund", true},{"faucetget", true}}; struct NSPV_ntzargs { @@ -517,11 +502,11 @@ int32_t NSPV_mempoolfuncs(bits256 *satoshisp,int32_t *vindexp,std::vectorfirst.txhash,tx,hashBlock); - std::vector oprets; uint256 tokenid,txid; - std::vector vopret,vOpretExtra; uint8_t *script,e,f; + std::vector> oprets; uint256 tokenid,txid; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; - if (DecodeTokenOpRetV1(tx.vout[tx.vout.size()-1].scriptPubKey,tokenid,pubkeys,oprets)!=0 && GetOpReturnCCBlob(oprets, vOpretExtra) && vOpretExtra.size()>0) + if (DecodeTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,tokenevalcode,tokenid,pubkeys,oprets)!=0 && GetOpretBlob(oprets, OPRETID_CHANNELSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) { vopret=vOpretExtra; } @@ -586,9 +571,11 @@ int32_t NSPV_mempoolfuncs(bits256 *satoshisp,int32_t *vindexp,std::vector request) // received a req NSPV_rwremoterpcresp(1,&response[1],&R,slen); pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; - LogPrint("nspv", "pushed NSPV_REMOTERPCRESP response method %s to peer %d\n", R.method, pfrom->id); - LogPrint("nspv-details", "NSPV_REMOTERPCRESP response details: json %s to peer %d\n", R.json, pfrom->id); - NSPV_remoterpc_purge(&R); } } diff --git a/src/komodo_nSPV_superlite.h b/src/komodo_nSPV_superlite.h index 7fd6f77cc45..3d56d44ba8a 100644 --- a/src/komodo_nSPV_superlite.h +++ b/src/komodo_nSPV_superlite.h @@ -17,8 +17,6 @@ #ifndef KOMODO_NSPVSUPERLITE_H #define KOMODO_NSPVSUPERLITE_H -#include "komodo_DEX.h" - // nSPV client. VERY simplistic "single threaded" networking model. for production GUI best to multithread, etc. // no caching, no optimizations, no reducing the number of ntzsproofs needed by detecting overlaps, etc. // advantage is that it is simpler to implement and understand to create a design for a more performant version @@ -141,13 +139,13 @@ void komodo_nSPVresp(CNode *pfrom,std::vector response) // received a r switch ( response[0] ) { case NSPV_INFORESP: - //fprintf(stderr,"got version.%d info response %u size.%d height.%d\n",NSPV_inforesult.version,timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status + fprintf(stderr,"got version.%d info response %u size.%d height.%d\n",NSPV_inforesult.version,timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status I = NSPV_inforesult; NSPV_inforesp_purge(&NSPV_inforesult); NSPV_rwinforesp(0,&response[1],&NSPV_inforesult); if ( NSPV_inforesult.height < I.height ) { - //fprintf(stderr,"got old info response %u size.%d height.%d\n",timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status + fprintf(stderr,"got old info response %u size.%d height.%d\n",timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status NSPV_inforesp_purge(&NSPV_inforesult); NSPV_inforesult = I; } diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 83504b14c56..a9ad290df00 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -12,9 +12,6 @@ * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ -#ifndef __KOMODO_UTIL_H__ -#define __KOMODO_UTIL_H__ - #include "komodo_defs.h" #include "key_io.h" #include "cc/CCinclude.h" @@ -26,8 +23,6 @@ #include #endif -#include "cc/pricesfeed.h" - #define SATOSHIDEN ((uint64_t)100000000L) #define dstr(x) ((double)(x) / SATOSHIDEN) #define portable_mutex_t pthread_mutex_t @@ -1550,8 +1545,6 @@ uint16_t komodo_port(char *symbol,uint64_t supply,uint32_t *magicp,uint8_t *extr printf("ports\n"); }*/ -char *NISTconfig = (char *)"[ {\"name\":\"nist\", \"url\":\"https://beacon.nist.gov/beacon/2.0/chain/1/pulse/last\", \"customlib\":\"libnistrandomparser\", \"results\":[{\"symbol\":\"pulseIndex\",\"customdata\":\"/pulse/pulseIndex\"}, {\"symbol\":\"pulseData0\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData1\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData2\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData3\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData4\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData5\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData6\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData7\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData8\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData9\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData10\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData11\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData12\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData13\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData14\",\"customdata\":\"/pulse/localRandomValue\"}, {\"symbol\":\"pulseData15\",\"customdata\":\"/pulse/localRandomValue\"} ], \"multiplier\":1, \"interval\":60 } ]"; - char *iguanafmtstr = (char *)"curl --url \"http://127.0.0.1:7776\" --data \"{\\\"conf\\\":\\\"%s.conf\\\",\\\"path\\\":\\\"${HOME#\"/\"}/.komodo/%s\\\",\\\"unitval\\\":\\\"20\\\",\\\"zcash\\\":1,\\\"RELAY\\\":-1,\\\"VALIDATE\\\":0,\\\"prefetchlag\\\":-1,\\\"poll\\\":100,\\\"active\\\":1,\\\"agent\\\":\\\"iguana\\\",\\\"method\\\":\\\"addcoin\\\",\\\"startpend\\\":4,\\\"endpend\\\":4,\\\"services\\\":129,\\\"maxpeers\\\":8,\\\"newcoin\\\":\\\"%s\\\",\\\"name\\\":\\\"%s\\\",\\\"hasheaders\\\":1,\\\"useaddmultisig\\\":0,\\\"netmagic\\\":\\\"%s\\\",\\\"p2p\\\":%u,\\\"rpc\\\":%u,\\\"pubval\\\":60,\\\"p2shval\\\":85,\\\"wifval\\\":188,\\\"txfee_satoshis\\\":\\\"10000\\\",\\\"isPoS\\\":0,\\\"minoutput\\\":10000,\\\"minconfirms\\\":2,\\\"genesishash\\\":\\\"027e3758c3a65b12aa1046462b486d0a63bfa1beae327897f56c5cfb7daaae71\\\",\\\"protover\\\":170002,\\\"genesisblock\\\":\\\"0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000000000000000000000000000000000000000000000000000000000000029ab5f490f0f0f200b00000000000000000000000000000000000000000000000000000000000000fd4005000d5ba7cda5d473947263bf194285317179d2b0d307119c2e7cc4bd8ac456f0774bd52b0cd9249be9d40718b6397a4c7bbd8f2b3272fed2823cd2af4bd1632200ba4bf796727d6347b225f670f292343274cc35099466f5fb5f0cd1c105121b28213d15db2ed7bdba490b4cedc69742a57b7c25af24485e523aadbb77a0144fc76f79ef73bd8530d42b9f3b9bed1c135ad1fe152923fafe98f95f76f1615e64c4abb1137f4c31b218ba2782bc15534788dda2cc08a0ee2987c8b27ff41bd4e31cd5fb5643dfe862c9a02ca9f90c8c51a6671d681d04ad47e4b53b1518d4befafefe8cadfb912f3d03051b1efbf1dfe37b56e93a741d8dfd80d576ca250bee55fab1311fc7b3255977558cdda6f7d6f875306e43a14413facdaed2f46093e0ef1e8f8a963e1632dcbeebd8e49fd16b57d49b08f9762de89157c65233f60c8e38a1f503a48c555f8ec45dedecd574a37601323c27be597b956343107f8bd80f3a925afaf30811df83c402116bb9c1e5231c70fff899a7c82f73c902ba54da53cc459b7bf1113db65cc8f6914d3618560ea69abd13658fa7b6af92d374d6eca9529f8bd565166e4fcbf2a8dfb3c9b69539d4d2ee2e9321b85b331925df195915f2757637c2805e1d4131e1ad9ef9bc1bb1c732d8dba4738716d351ab30c996c8657bab39567ee3b29c6d054b711495c0d52e1cd5d8e55b4f0f0325b97369280755b46a02afd54be4ddd9f77c22272b8bbb17ff5118fedbae2564524e797bd28b5f74f7079d532ccc059807989f94d267f47e724b3f1ecfe00ec9e6541c961080d8891251b84b4480bc292f6a180bea089fef5bbda56e1e41390d7c0e85ba0ef530f7177413481a226465a36ef6afe1e2bca69d2078712b3912bba1a99b1fbff0d355d6ffe726d2bb6fbc103c4ac5756e5bee6e47e17424ebcbf1b63d8cb90ce2e40198b4f4198689daea254307e52a25562f4c1455340f0ffeb10f9d8e914775e37d0edca019fb1b9c6ef81255ed86bc51c5391e0591480f66e2d88c5f4fd7277697968656a9b113ab97f874fdd5f2465e5559533e01ba13ef4a8f7a21d02c30c8ded68e8c54603ab9c8084ef6d9eb4e92c75b078539e2ae786ebab6dab73a09e0aa9ac575bcefb29e930ae656e58bcb513f7e3c17e079dce4f05b5dbc18c2a872b22509740ebe6a3903e00ad1abc55076441862643f93606e3dc35e8d9f2caef3ee6be14d513b2e062b21d0061de3bd56881713a1a5c17f5ace05e1ec09da53f99442df175a49bd154aa96e4949decd52fed79ccf7ccbce32941419c314e374e4a396ac553e17b5340336a1a25c22f9e42a243ba5404450b650acfc826a6e432971ace776e15719515e1634ceb9a4a35061b668c74998d3dfb5827f6238ec015377e6f9c94f38108768cf6e5c8b132e0303fb5a200368f845ad9d46343035a6ff94031df8d8309415bb3f6cd5ede9c135fdabcc030599858d803c0f85be7661c88984d88faa3d26fb0e9aac0056a53f1b5d0baed713c853c4a2726869a0a124a8a5bbc0fc0ef80c8ae4cb53636aa02503b86a1eb9836fcc259823e2692d921d88e1ffc1e6cb2bde43939ceb3f32a611686f539f8f7c9f0bf00381f743607d40960f06d347d1cd8ac8a51969c25e37150efdf7aa4c2037a2fd0516fb444525ab157a0ed0a7412b2fa69b217fe397263153782c0f64351fbdf2678fa0dc8569912dcd8e3ccad38f34f23bbbce14c6a26ac24911b308b82c7e43062d180baeac4ba7153858365c72c63dcf5f6a5b08070b730adb017aeae925b7d0439979e2679f45ed2f25a7edcfd2fb77a8794630285ccb0a071f5cce410b46dbf9750b0354aae8b65574501cc69efb5b6a43444074fee116641bb29da56c2b4a7f456991fc92b2\\\",\\\"debug\\\":0,\\\"seedipaddr\\\":\\\"%s\\\",\\\"sapling\\\":1,\\\"notarypay\\\":%i}\""; @@ -1705,7 +1698,7 @@ int8_t equihash_params_possible(uint64_t n, uint64_t k) void komodo_args(char *argv0) { std::string name,addn,hexstr,symbol; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[32756],disablebits[32],*extraptr=0; - FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256], ccEnablesHeight[512] = {0}, cczerotxfee[256]; CTransaction earlytx; uint256 hashBlock; + FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256], ccEnablesHeight[512] = {0}; CTransaction earlytx; uint256 hashBlock; IS_KOMODO_NOTARY = GetBoolArg("-notary", false); IS_STAKED_NOTARY = GetArg("-stakednotary", -1); @@ -1713,7 +1706,6 @@ void komodo_args(char *argv0) memset(ccenables,0,sizeof(ccenables)); memset(disablebits,0,sizeof(disablebits)); memset(ccEnablesHeight,0,sizeof(ccEnablesHeight)); - memset(cczerotxfee,0,sizeof(cczerotxfee)); if ( GetBoolArg("-gen", false) != 0 ) { KOMODO_MININGTHREADS = GetArg("-genproclimit",-1); @@ -1767,9 +1759,6 @@ void komodo_args(char *argv0) } } } - - strncpy(ASSETCHAINS_SYMBOL,name.c_str(),sizeof(ASSETCHAINS_SYMBOL)-1); - KOMODO_STOPAT = GetArg("-stopat",0); MAX_REORG_LENGTH = GetArg("-maxreorg",MAX_REORG_LENGTH); WITNESS_CACHE_SIZE = MAX_REORG_LENGTH+10; @@ -1809,12 +1798,6 @@ void komodo_args(char *argv0) } KOMODO_EARLYTXID = Parseuint256(GetArg("-earlytxid","0").c_str()); ASSETCHAINS_EARLYTXIDCONTRACT = GetArg("-ac_earlytxidcontract",0); - Split(GetArg("-cczerotxfee",""), sizeof(cczerotxfee), cczerotxfee, 0); - memset(ASSETCHAINS_CCZEROTXFEE,0,sizeof(ASSETCHAINS_CCZEROTXFEE)); - for (i=0; i<256; i++) - { - if ( cczerotxfee[i]!=0) ASSETCHAINS_CCZEROTXFEE[cczerotxfee[i]]=1; - } if ( name.c_str()[0] != 0 ) { std::string selectedAlgo = GetArg("-ac_algo", std::string(ASSETCHAINS_ALGORITHMS[0])); @@ -1894,7 +1877,6 @@ void komodo_args(char *argv0) } fprintf(stderr,"ASSETCHAINS_SUPPLY %llu\n",(long long)ASSETCHAINS_SUPPLY); - KOMODO_DEX_P2P = GetArg("-dexp2p",0); // 1 normal node, 2 full node ASSETCHAINS_COMMISSION = GetArg("-ac_perc",0); ASSETCHAINS_OVERRIDE_PUBKEY = GetArg("-ac_pubkey",""); ASSETCHAINS_SCRIPTPUB = GetArg("-ac_script",""); @@ -1907,71 +1889,18 @@ void komodo_args(char *argv0) //fprintf(stderr,"ASSETCHAINS_CBOPRET.%llx\n",(long long)ASSETCHAINS_CBOPRET); if ( ASSETCHAINS_CBOPRET != 0 ) { - std::vector ac_forex = { "BGN", "NZD", "ILS", "RUB", "CAD", "PHP", "CHF", "AUD", "JPY", "TRY", "HKD", "MYR", "HRK", "CZK", "IDR", "DKK", "NOK", "HUF", "GBP", "MXN", "THB", "ISK", "ZAR", "BRL", "SGD", "PLN", "INR", "KRW", "RON", "CNY", "SEK", "EUR" }; - std::vector ac_prices; - std::vector ac_stocks; - - SplitStr(GetArg("-ac_prices", ""), ac_prices); - if (ac_prices.size() > 0) + SplitStr(GetArg("-ac_prices",""), ASSETCHAINS_PRICES); + if ( ASSETCHAINS_PRICES.size() > 0 ) ASSETCHAINS_CBOPRET |= 4; - SplitStr(GetArg("-ac_stocks", ""), ac_stocks); - if (ac_stocks.size() > 0) + SplitStr(GetArg("-ac_stocks",""), ASSETCHAINS_STOCKS); + if ( ASSETCHAINS_STOCKS.size() > 0 ) ASSETCHAINS_CBOPRET |= 8; - for (i = 0; i < ac_prices.size(); i ++) - fprintf(stderr, "%s ", ac_prices[i].c_str()); - fprintf(stderr, "%d -ac_prices\n", (int32_t)ac_prices.size()); - for (i = 0; i < ac_stocks.size(); i ++) - fprintf(stderr, "%s ", ac_stocks[i].c_str()); - fprintf(stderr, "%d -ac_stocks\n", (int32_t)ac_stocks.size()); - - // parsing -ac_feeds config - std::string sfeedcfg = GetArg("-ac_feeds", ""); - if (!sfeedcfg.empty()) - { - bool parsed = false; - if ( strcmp(sfeedcfg.c_str(),"NIST") == 0 ) - sfeedcfg = NISTconfig; - cJSON *jfeedcfg = cJSON_Parse(sfeedcfg.c_str()); - if (jfeedcfg) { - parsed = PricesFeedParseConfig(jfeedcfg); - fprintf(stderr,"prices feed (%s)\n",jprint(jfeedcfg,0)); - cJSON_Delete(jfeedcfg); - } - else - { - LOGSTREAM("prices", CCLOG_ERROR, stream << "could not parse json from -ac_feeds" << std::endl); - } - - if (!parsed) { - std::cerr << "ERROR: could not parse -ac_feeds config (check debug.log), shutdown\n"; - StartShutdown(); - } - } - - // checking blocktime - if (ASSETCHAINS_BLOCKTIME < PF_MININTERVAL )// PF_DEFAULTINTERVAL + 60) - { - LOGSTREAM("prices", CCLOG_ERROR, stream << "blocktime too low for prices to work normally" << std::endl); - std::cerr << "ERROR: blocktime too low for prices to work normally, restart the node with blocktime >= 60\n"; - // StartShutdown(); - } - - // add old-style prices config - if (ASSETCHAINS_CBOPRET & 2) - PricesAddOldForexConfig(ac_forex); - if ((ASSETCHAINS_CBOPRET & 4) || ac_prices.size() > 0) // if only ASSETCHAINS_CBOPRET & 4 then add default prices KMD_BTC and ETH_BTC - PricesAddOldPricesConfig(ac_prices); - if (ac_stocks.size() > 0) - PricesAddOldStocksConfig(ac_stocks); - - // init poll buffers - if (!PricesInitStatuses()) - { - std::cerr << "error prices initializing (check debug.log), shutdown\n"; - StartShutdown(); - } - - fprintf(stderr, "%d -ac_feeds\n", PricesFeedSymbolsCount()); // print size with default prices + for (i=0; i0 || ASSETCHAINS_COMMISSION!=0)) { - fprintf(stderr,"when using gateway import these must be set: -ac_end=1 -ac_perc=0\n"); + fprintf(stderr,"when using gateway import these must be set: -ac_end=1 -ac_supply=0 -ac_perc=0\n"); StartShutdown(); } @@ -2253,21 +2182,27 @@ fprintf(stderr,"extralen.%d before disable bits\n",extralen); if ( ASSETCHAINS_CBOPRET != 0 ) { extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_CBOPRET),(void *)&ASSETCHAINS_CBOPRET); - if (PricesFeedSymbolsCount() > 0) { - // add price names params for magic calc: - std::string feednames; - - // if 7 or 15 provide magic compatibility with old prices which includes only -ac_prices and -ac_stocks into magic: - bool oldPricesCompatible = ASSETCHAINS_CBOPRET == 7 || ASSETCHAINS_CBOPRET == 15 ? true : false; - PricesFeedSymbolsForMagic(feednames, oldPricesCompatible); - assert(extralen + feednames.length() < sizeof(extrabuf) / sizeof(extrabuf[0])); - memcpy(&extraptr[extralen], feednames.c_str(), feednames.length()); - extralen += feednames.length(); + if ( ASSETCHAINS_PRICES.size() != 0 ) + { + for (i=0; i 0 ) ASSETCHAINS_SEED = 1; - // moved up - // strncpy(ASSETCHAINS_SYMBOL,name.c_str(),sizeof(ASSETCHAINS_SYMBOL)-1); + strncpy(ASSETCHAINS_SYMBOL,name.c_str(),sizeof(ASSETCHAINS_SYMBOL)-1); MAX_MONEY = komodo_max_money(); @@ -2559,5 +2493,3 @@ void komodo_prefetch(FILE *fp) } fseek(fp,fpos,SEEK_SET); } - -#endif // #ifndef __KOMODO_UTIL_H__ diff --git a/src/main.cpp b/src/main.cpp index 407cafedba5..684c1a7b48f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Zcash developers +// Copyright (c) 2015-2020 The Komodo Platform developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -920,7 +922,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { reason = "bare-multisig"; return false; - } else if (!IsCryptoConditionsEnabled() && txout.IsDust(::minRelayTxFee)) { + } else if (whichType != TX_CRYPTOCONDITION && txout.IsDust(::minRelayTxFee)) { reason = "dust"; return false; } @@ -1006,7 +1008,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // However this changes once median past time-locks are enforced: const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) ? chainActive.Tip()->GetMedianTimePast() - : GetAdjustedTime(); + : GetTime(); return IsFinalTx(tx, nBlockHeight, nBlockTime); } @@ -2265,8 +2267,7 @@ bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlo } hashBlock = header.GetHash(); if (txOut.GetHash() != hash) - //return error("%s: txid mismatch", __func__); - return error("%s: txid mismatch on disk=%s param=%s", __func__, txOut.GetHash().GetHex().c_str(), hash.GetHex().c_str()); //dimxy added + return error("%s: txid mismatch", __func__); //fprintf(stderr,"found on disk %s\n",hash.GetHex().c_str()); return true; } @@ -2322,8 +2323,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock } hashBlock = header.GetHash(); if (txOut.GetHash() != hash) - //return error("%s: txid mismatch", __func__); - return error("%s: txid mismatch on disk=%s param=%s", __func__, txOut.GetHash().GetHex().c_str(), hash.GetHex().c_str()); //dimxy added + return error("%s: txid mismatch", __func__); return true; } } @@ -3358,7 +3358,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const if (bestHeader == NULL || initialDownloadCheck()) return; static int64_t lastAlertTime = 0; - int64_t now = GetAdjustedTime(); + int64_t now = GetTime(); if (lastAlertTime > now-60*60*24) return; // Alert at most once per day const int SPAN_HOURS=4; @@ -3368,7 +3368,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const boost::math::poisson_distribution poisson(BLOCKS_EXPECTED); std::string strWarning; - int64_t startTime = GetAdjustedTime()-SPAN_SECONDS; + int64_t startTime = GetTime()-SPAN_SECONDS; LOCK(cs); const CBlockIndex* i = bestHeader; @@ -5079,13 +5079,13 @@ bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex, *futureblockp = 0; if ( ASSETCHAINS_ADAPTIVEPOW > 0 ) { - if (blockhdr.GetBlockTime() > GetAdjustedTime() + 4) + if (blockhdr.GetBlockTime() > GetTime() + 4) { //LogPrintf("CheckBlockHeader block from future %d error",blockhdr.GetBlockTime() - GetAdjustedTime()); return false; } } - else if (blockhdr.GetBlockTime() > GetAdjustedTime() + 60) + else if (blockhdr.GetBlockTime() > GetTime() + 60) { /*CBlockIndex *tipindex; //fprintf(stderr,"ht.%d future block %u vs time.%u + 60\n",height,(uint32_t)blockhdr.GetBlockTime(),(uint32_t)GetAdjustedTime()); @@ -5098,7 +5098,7 @@ bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex, } else*/ { - if (blockhdr.GetBlockTime() < GetAdjustedTime() + 300) + if (blockhdr.GetBlockTime() < GetTime() + 300) *futureblockp = 1; //LogPrintf("CheckBlockHeader block from future %d error",blockhdr.GetBlockTime() - GetAdjustedTime()); return false; //state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"),REJECT_INVALID, "time-too-new"); @@ -5367,7 +5367,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta } // Check that timestamp is not too far in the future - if (block.GetBlockTime() > GetAdjustedTime() + consensusParams.nMaxFutureBlockTime) + if (block.GetBlockTime() > GetTime() + consensusParams.nMaxFutureBlockTime) { return state.Invalid(error("%s: block timestamp too far in the future", __func__), REJECT_INVALID, "time-too-new"); @@ -7306,9 +7306,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->nStartingHeight, addrMe.ToString(), pfrom->id, remoteAddr); - int64_t nTimeOffset = nTime - GetTime(); - pfrom->nTimeOffset = nTimeOffset; - AddTimeData(pfrom->addr, nTimeOffset); + pfrom->nTimeOffset = timeWarning.AddTimeData(pfrom->addr, nTime, GetTime()); } @@ -7342,11 +7340,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; return false; } - if ( KOMODO_DEX_P2P != 0 && (pfrom->nServices & NODE_DEXP2P) == 0 ) - { - pfrom->fDisconnect = true; - return false; - } } // Mark this node as currently connected, so we update its timestamp later. if (pfrom->fNetworkNode) { @@ -7389,7 +7382,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Store the new addresses vector vAddrOk; - int64_t nNow = GetAdjustedTime(); + int64_t nNow = GetTime(); int64_t nSince = nNow - 10 * 60; BOOST_FOREACH(CAddress& addr, vAddr) { @@ -7557,16 +7550,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } return(true); } - else if ( strCommand == "DEX" ) - { - if ( KOMODO_DEX_P2P != 0 ) - { - std::vector payload; - vRecv >> payload; - komodo_DEXmsg(pfrom,payload); - } - return(true); - } else if ( KOMODO_NSPV_SUPERLITE ) return(true); else if (strCommand == "inv") @@ -7609,7 +7592,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // not a direct successor. pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexBestHeader), inv.hash); CNodeState *nodestate = State(pfrom->GetId()); - if (chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - chainparams.GetConsensus().nPowTargetSpacing * 20 && + if (chainActive.Tip()->GetBlockTime() > GetTime() - chainparams.GetConsensus().nPowTargetSpacing * 20 && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { vToFetch.push_back(inv); // Mark block as in flight already, even though the actual "getdata" message only goes out @@ -8390,14 +8373,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } state.fShouldBan = false; } - if ( KOMODO_DEX_P2P != 0 && (pto->nServices & NODE_DEXP2P) != 0 ) - komodo_DEXpoll(pto); if ( KOMODO_NSPV_SUPERLITE ) { komodo_nSPV(pto); return(true); } - BOOST_FOREACH(const CBlockReject& reject, state.rejects) pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); state.rejects.clear(); @@ -8408,7 +8388,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do. if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex && pindexBestHeader!=0) { // Only actively request headers from a single peer, unless we're close to today. - if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) { + if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetTime() - 24 * 60 * 60) { state.fSyncStarted = true; nSyncStarted++; CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader; diff --git a/src/miner.cpp b/src/miner.cpp index 467afc363ae..259df6ebbee 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -64,8 +64,6 @@ #endif #include -#include "cc/CCMarmara.h" - using namespace std; ////////////////////////////////////////////////////////////////////////////// @@ -126,8 +124,8 @@ class TxPriorityCompare void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { if ( ASSETCHAINS_ADAPTIVEPOW <= 0 ) - pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - else pblock->nTime = std::max((int64_t)(pindexPrev->nTime+1), GetAdjustedTime()); + pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetTime()); + else pblock->nTime = std::max((int64_t)(pindexPrev->nTime+1), GetTime()); // Updating time can change work required on testnet: if (ASSETCHAINS_ADAPTIVEPOW > 0 || consensusParams.nPowAllowMinDifficultyBlocksAfterHeight != boost::none) @@ -157,6 +155,8 @@ int32_t komodo_newStakerActive(int32_t height, uint32_t timestamp); int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33, void* ptr); int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); int32_t komodo_is_notarytx(const CTransaction& tx); +CScript Marmara_scriptPubKey(int32_t height,CPubKey pk); +CScript MarmaraCoinbaseOpret(uint8_t funcid,int32_t height,CPubKey pk); uint64_t komodo_notarypay(CMutableTransaction &txNew, std::vector &NotarisationNotaries, uint32_t timestamp, int32_t height, uint8_t *script, int32_t len); int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); int32_t komodo_getnotarizedheight(uint32_t timestamp,int32_t height, uint8_t *script, int32_t len); @@ -166,7 +166,7 @@ CScript komodo_makeopret(CBlock *pblock, bool fNew); int32_t komodo_waituntilelegible(uint32_t blocktime, int32_t stakeHeight, uint32_t delay) { - int64_t adjustedtime = (int64_t)GetAdjustedTime(); + int64_t adjustedtime = (int64_t)GetTime(); while ( (int64_t)blocktime-ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX > adjustedtime ) { int64_t secToElegible = (int64_t)blocktime-ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX-adjustedtime; @@ -182,7 +182,7 @@ int32_t komodo_waituntilelegible(uint32_t blocktime, int32_t stakeHeight, uint32 if( !GetBoolArg("-gen",false) ) return(0); sleep(1); - adjustedtime = (int64_t)GetAdjustedTime(); + adjustedtime = (int64_t)GetTime(); } return(1); } @@ -192,35 +192,34 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 CScript scriptPubKeyIn(_scriptPubKeyIn); CPubKey pk; - if (_pk.size() != 33) + if ( _pk.size() != 33 ) { pk = CPubKey(); std::vector> vAddrs; txnouttype txT; - if (scriptPubKeyIn.size() > 0 && Solver(scriptPubKeyIn, txT, vAddrs)) + if ( scriptPubKeyIn.size() > 0 && Solver(scriptPubKeyIn, txT, vAddrs)) { if (txT == TX_PUBKEY) pk = CPubKey(vAddrs[0]); } - } - else pk = _pk; + } else pk = _pk; - uint64_t deposits, voutsum = 0; int32_t isrealtime, kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params(); + uint64_t deposits,voutsum=0; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params(); bool fNotarisationBlock = false; std::vector NotarisationNotaries; - + //fprintf(stderr,"create new block\n"); // Create new block - if (gpucount < 0) + if ( gpucount < 0 ) gpucount = KOMODO_MAXGPUCOUNT; std::unique_ptr pblocktemplate(new CBlockTemplate()); - if (!pblocktemplate.get()) + if(!pblocktemplate.get()) { - fprintf(stderr, "pblocktemplate.get() failure\n"); + fprintf(stderr,"pblocktemplate.get() failure\n"); return NULL; } CBlock *pblock = &pblocktemplate->block; // pointer for convenience - // -regtest only: allow overriding block.nVersion with - // -blockversion=N to test forking scenarios + // -regtest only: allow overriding block.nVersion with + // -blockversion=N to test forking scenarios if (Params().MineBlocksOnDemand()) pblock->nVersion = GetArg("-blockversion", pblock->nVersion); @@ -229,10 +228,10 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 pblocktemplate->vTxFees.push_back(-1); // updated at end pblocktemplate->vTxSigOps.push_back(-1); // updated at end - // Largest block you're willing to create: - unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE(chainActive.LastTip()->GetHeight() + 1)); + // Largest block you're willing to create: + unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE(chainActive.LastTip()->GetHeight()+1)); // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: - nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE(chainActive.LastTip()->GetHeight() + 1) - 1000), nBlockMaxSize)); + nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE(chainActive.LastTip()->GetHeight()+1)-1000), nBlockMaxSize)); // How much of the block should be dedicated to high-priority transactions, // included regardless of the fees they pay @@ -252,9 +251,9 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 boost::optional cheatSpend; uint256 cbHash; - + boost::this_thread::interruption_point(); // exit thread before entering locks. - + CBlockIndex* pindexPrev = 0; { // this should stop create block ever exiting until it has returned something. @@ -268,8 +267,8 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 bool sapling = NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_SAPLING); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); - uint32_t proposedTime = GetAdjustedTime(); - voutsum = GetBlockSubsidy(nHeight, consensusParams) + 10000 * COIN; // approx fees + uint32_t proposedTime = GetTime(); + voutsum = GetBlockSubsidy(nHeight,consensusParams) + 10000*COIN; // approx fees if (proposedTime == nMedianTimePast) { @@ -277,15 +276,15 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 // forward as quickly as possible for (int i; i < 100; i++) { - proposedTime = GetAdjustedTime(); + proposedTime = GetTime(); if (proposedTime == nMedianTimePast) MilliSleep(10); } } - pblock->nTime = GetAdjustedTime(); + pblock->nTime = GetTime(); // Now we have the block time + height, we can get the active notaries. - int8_t numSN = 0; uint8_t notarypubkeys[64][33] = { 0 }; - if (ASSETCHAINS_NOTARY_PAY[0] != 0) + int8_t numSN = 0; uint8_t notarypubkeys[64][33] = {0}; + if ( ASSETCHAINS_NOTARY_PAY[0] != 0 ) { // Only use speical miner for notary pay chains. numSN = komodo_notaries(notarypubkeys, nHeight, pblock->nTime); @@ -293,7 +292,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 CCoinsViewCache view(pcoinsTip); uint32_t expired; uint64_t commission; - + SaplingMerkleTree sapling_tree; assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree)); @@ -309,14 +308,14 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 // now add transactions from the mem pool int32_t Notarisations = 0; uint64_t txvalue; for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); - mi != mempool.mapTx.end(); ++mi) + mi != mempool.mapTx.end(); ++mi) { //break; // dont add any tx to block.. debug for KMD fix. Disabled. const CTransaction& tx = mi->GetTx(); int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) - ? nMedianTimePast - : pblock->GetBlockTime(); + ? nMedianTimePast + : pblock->GetBlockTime(); if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight)) { @@ -324,12 +323,12 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 continue; } txvalue = tx.GetValueOut(); - if (KOMODO_VALUETOOBIG(txvalue) != 0) + if ( KOMODO_VALUETOOBIG(txvalue) != 0 ) continue; //if ( KOMODO_VALUETOOBIG(txvalue + voutsum) != 0 ) // has been commented from main.cpp ? // continue; //voutsum += txvalue; - if (ASSETCHAINS_SYMBOL[0] == 0 && komodo_validate_interest(tx, nHeight, (uint32_t)pblock->nTime, 0) < 0) + if ( ASSETCHAINS_SYMBOL[0] == 0 && komodo_validate_interest(tx,nHeight,(uint32_t)pblock->nTime,0) < 0 ) { fprintf(stderr,"CreateNewBlock: komodo_validate_interest failure txid.%s nHeight.%d nTime.%u vs locktime.%u\n",tx.GetHash().ToString().c_str(),nHeight,(uint32_t)pblock->nTime,(uint32_t)tx.nLockTime); continue; @@ -346,16 +345,15 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 CAmount nValueIn = GetCoinImportValue(tx); // burn amount nTotalIn += nValueIn; dPriority += (double)nValueIn * 1000; // flat multiplier... max = 1e16. - } - else { + } else { TMP_NotarisationNotaries.clear(); bool fToCryptoAddress = false; - if (numSN != 0 && notarypubkeys[0][0] != 0 && komodo_is_notarytx(tx) == 1) + if ( numSN != 0 && notarypubkeys[0][0] != 0 && komodo_is_notarytx(tx) == 1 ) fToCryptoAddress = true; BOOST_FOREACH(const CTxIn& txin, tx.vin) { - if (tx.IsPegsImport() && txin.prevout.n == 10e8) + if (tx.IsPegsImport() && txin.prevout.n==10e8) { CAmount nValueIn = GetCoinImportValue(tx); // burn amount nTotalIn += nValueIn; @@ -397,33 +395,32 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 nTotalIn += nValueIn; int nConf = nHeight - coins->nHeight; - + uint8_t *script; int32_t scriptlen; uint256 hash; CTransaction tx1; // loop over notaries array and extract index of signers. if ( fToCryptoAddress && myGetTransaction(txin.prevout.hash,tx1,hash) ) { - for (int8_t i = 0; i < numSN; i++) + for (int8_t i = 0; i < numSN; i++) { script = (uint8_t *)&tx1.vout[txin.prevout.n].scriptPubKey[0]; scriptlen = (int32_t)tx1.vout[txin.prevout.n].scriptPubKey.size(); - if (scriptlen == 35 && script[0] == 33 && script[34] == OP_CHECKSIG && memcmp(script + 1, notarypubkeys[i], 33) == 0) + if ( scriptlen == 35 && script[0] == 33 && script[34] == OP_CHECKSIG && memcmp(script+1,notarypubkeys[i],33) == 0 ) { // We can add the index of each notary to vector, and clear it if this notarisation is not valid later on. - TMP_NotarisationNotaries.push_back(i); + TMP_NotarisationNotaries.push_back(i); } } } dPriority += (double)nValueIn * nConf; } - if (numSN != 0 && notarypubkeys[0][0] != 0 && TMP_NotarisationNotaries.size() >= numSN / 5) + if ( numSN != 0 && notarypubkeys[0][0] != 0 && TMP_NotarisationNotaries.size() >= numSN / 5 ) { // check a notary didnt sign twice (this would be an invalid notarisation later on and cause problems) - std::set checkdupes(TMP_NotarisationNotaries.begin(), TMP_NotarisationNotaries.end()); - if (checkdupes.size() != TMP_NotarisationNotaries.size()) + std::set checkdupes( TMP_NotarisationNotaries.begin(), TMP_NotarisationNotaries.end() ); + if ( checkdupes.size() != TMP_NotarisationNotaries.size() ) { fprintf(stderr, "possible notarisation is signed multiple times by same notary, passed as normal transaction.\n"); - } - else fNotarisation = true; + } else fNotarisation = true; } nTotalIn += tx.GetShieldedValueIn(); } @@ -434,34 +431,30 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); dPriority = tx.ComputePriority(dPriority, nTxSize); - if (tx.IsPriorityCC()) dPriority=1e16; - uint256 hash = tx.GetHash(); mempool.ApplyDeltas(hash, dPriority, nTotalIn); - CFeeRate feeRate(nTotalIn - tx.GetValueOut(), nTxSize); - - if (tx.IsPriorityCC()) dPriority=1e16; + CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize); - if (fNotarisation) + if ( fNotarisation ) { // Special miner for notary pay chains. Can only enter this if numSN/notarypubkeys is set higher up. - if (tx.vout.size() == 2 && tx.vout[1].nValue == 0) + if ( tx.vout.size() == 2 && tx.vout[1].nValue == 0 ) { // Get the OP_RETURN for the notarisation uint8_t *script = (uint8_t *)&tx.vout[1].scriptPubKey[0]; int32_t scriptlen = (int32_t)tx.vout[1].scriptPubKey.size(); - if (script[0] == OP_RETURN) + if ( script[0] == OP_RETURN ) { Notarisations++; - if (Notarisations > 1) + if ( Notarisations > 1 ) { - fprintf(stderr, "skipping notarization.%d\n", Notarisations); + fprintf(stderr, "skipping notarization.%d\n",Notarisations); // Any attempted notarization needs to be in its own block! continue; } int32_t notarizedheight = komodo_getnotarizedheight(pblock->nTime, nHeight, script, scriptlen); - if (notarizedheight != 0) + if ( notarizedheight != 0 ) { // this is the first one we see, add it to the block as TX1 NotarisationNotaries = TMP_NotarisationNotaries; @@ -472,7 +465,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 } } } - else if (dPriority == 1e16) + else if ( dPriority == 1e16 ) { dPriority -= 10; // make sure notarisation is tx[1] in block. @@ -559,7 +552,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 const uint256& hash = tx.GetHash(); double dPriorityDelta = 0; CAmount nFeeDelta = 0; - mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); // ApplyDeltas will not find miner's transactions + mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize)) { //fprintf(stderr,"fee rate skip\n"); @@ -639,8 +632,8 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; if ( ASSETCHAINS_ADAPTIVEPOW <= 0 ) - blocktime = 1 + std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - else blocktime = 1 + std::max((int64_t)(pindexPrev->nTime+1), GetAdjustedTime()); + blocktime = 1 + std::max(pindexPrev->GetMedianTimePast()+1, GetTime()); + else blocktime = 1 + std::max((int64_t)(pindexPrev->nTime+1), GetTime()); //pblock->nTime = blocktime + 1; pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); //fprintf(stderr, "nBits.%u\n",pblock->nBits); @@ -661,7 +654,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 arith_uint256 posHash; siglen = verus_staked(pblock, txStaked, nBitsPOS, posHash, utxosig, pk); - blocktime = GetAdjustedTime(); + blocktime = GetTime(); // change the scriptPubKeyIn to the same output script exactly as the staking transaction if (siglen > 0) @@ -669,7 +662,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 } else { - blocktime = GetAdjustedTime(); + blocktime = GetTime(); uint256 merkleroot = komodo_calcmerkleroot(pblock, pindexPrev->GetBlockHash(), nHeight, true, scriptPubKeyIn); //fprintf(stderr, "MINER: merkleroot.%s\n", merkleroot.GetHex().c_str()); /* @@ -690,26 +683,12 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 } siglen = komodo_staked(txStaked, pblock->nBits, &blocktime, &txtime, &utxotxid, &utxovout, &utxovalue, utxosig, merkleroot); - if ( komodo_newStakerActive(nHeight, blocktime) != 0 && !ASSETCHAINS_MARMARA) - nFees += utxovalue; // this adds utxovalue to coinbase that causes 'Coinbase pays too much' errors. I temporarily turned this off for marmara + if ( komodo_newStakerActive(nHeight, blocktime) != 0 ) + nFees += utxovalue; //fprintf(stderr, "added to coinbase.%llu staking tx valueout.%llu\n", (long long unsigned)utxovalue, (long long unsigned)txStaked.vout[0].nValue); uint32_t delay = ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH ? ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX : ASSETCHAINS_STAKED_BLOCK_FUTURE_HALF; if ( komodo_waituntilelegible(blocktime, stakeHeight, delay) == 0 ) return(0); - - if (ASSETCHAINS_MARMARA && nHeight > 0 && (nHeight & 1) == 0) - { - CScript EncodeStakingOpRet(uint256 merkleroot); - - // update coinbase spk based on marmara staked tx and and recalculate staked tx merkle root: - scriptPubKeyIn = MarmaraCreatePoSCoinbaseScriptPubKey(nHeight, scriptPubKeyIn, txStaked); - uint256 merkleroot = komodo_calcmerkleroot(pblock, pindexPrev->GetBlockHash(), nHeight, true, scriptPubKeyIn); - if (txStaked.vout.size() == 2) { // merkle opret was created - txStaked.vout[1].scriptPubKey = EncodeStakingOpRet(merkleroot); - siglen = MarmaraSignature(utxosig, txStaked); // add marmara opret and sign the stake tx - LOGSTREAMFN("marmara", CCLOG_DEBUG1, stream << "updated coinbase scriptPubKeyIn and merkleroot in staked tx for height=" << nHeight << std::endl); - } - } } if ( siglen > 0 ) @@ -742,16 +721,21 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 //if ((uint32_t)chainActive.LastTip()->nTime < ASSETCHAINS_STAKED_HF_TIMESTAMP) { if ( ASSETCHAINS_ADAPTIVEPOW <= 0 ) - txNew.nLockTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - else txNew.nLockTime = std::max((int64_t)(pindexPrev->nTime+1), GetAdjustedTime()); + txNew.nLockTime = std::max(pindexPrev->GetMedianTimePast()+1, GetTime()); + else txNew.nLockTime = std::max((int64_t)(pindexPrev->nTime+1), GetTime()); if ( ASSETCHAINS_SYMBOL[0] == 0 && IS_KOMODO_NOTARY != 0 && My_notaryid >= 0 ) txNew.vout[0].nValue += 5000; pblock->vtx[0] = txNew; - if (ASSETCHAINS_MARMARA && nHeight > 0 && (nHeight & 1) == 0) - { // add marmara coinbase opret for activated coins (for even blocks) - // MarmaraCreatePoSCoinbaseScriptPubKey(txNew, nHeight, pk, isStake, pblock->vtx.back()); + if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 ) + { + char checkaddr[64]; + Getscriptaddress(checkaddr,txNew.vout[0].scriptPubKey); + //`fprintf(stderr,"set mining coinbase -> %s\n",checkaddr); + txNew.vout.resize(2); + txNew.vout[1].nValue = 0; + txNew.vout[1].scriptPubKey = MarmaraCoinbaseOpret('C',nHeight,pk); } else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0) && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0 ) { @@ -1063,11 +1047,8 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, scriptPubKey[34] = OP_CHECKSIG; } } - if (ASSETCHAINS_MARMARA) - { - // create marmara activated coins spk for even blocks - scriptPubKey = MarmaraCreateDefaultCoinbaseScriptPubKey(nHeight, pubkey); - } + if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 ) + scriptPubKey = Marmara_scriptPubKey(nHeight,pubkey); return CreateNewBlock(pubkey, scriptPubKey, gpucount, isStake); } @@ -1994,10 +1975,10 @@ void static BitcoinMiner() // MilliSleep(30); return false; } - if ( IS_KOMODO_NOTARY != 0 && B.nTime > GetAdjustedTime() ) + if ( IS_KOMODO_NOTARY != 0 && B.nTime > GetTime() ) { //fprintf(stderr,"need to wait %d seconds to submit block\n",(int32_t)(B.nTime - GetAdjustedTime())); - while ( GetAdjustedTime() < B.nTime-2 ) + while ( GetTime() < B.nTime-2 ) { sleep(1); if ( chainActive.LastTip()->GetHeight() >= Mining_height ) diff --git a/src/net.cpp b/src/net.cpp index 1ac3e5a22d6..4c221cd45ce 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -192,7 +192,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer) ret = CAddress(addr); } ret.nServices = nLocalServices; - ret.nTime = GetAdjustedTime(); + ret.nTime = GetTime(); return ret; } @@ -392,7 +392,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) /// debug print LogPrint("net", "trying connection %s lastseen=%.1fhrs\n", pszDest ? pszDest : addrConnect.ToString(), - pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); + pszDest ? 0.0 : (double)(GetTime() - addrConnect.nTime)/3600.0); // Connect SOCKET hSocket; @@ -444,7 +444,7 @@ void CNode::CloseSocketDisconnect() vRecvMsg.clear(); } -extern int32_t KOMODO_NSPV,KOMODO_DEX_P2P; +extern int32_t KOMODO_NSPV; #ifndef KOMODO_NSPV_FULLNODE #define KOMODO_NSPV_FULLNODE (KOMODO_NSPV <= 0) #endif // !KOMODO_NSPV_FULLNODE @@ -457,7 +457,7 @@ void CNode::PushVersion() { int nBestHeight = g_signals.GetHeight().get_value_or(0); - int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime()); + int64_t nTime = (fInbound ? GetTime() : GetTime()); CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); CAddress addrMe = GetLocalAddress(&addr); GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); @@ -567,10 +567,13 @@ void CNode::AddWhitelistedRange(const CSubNet &subnet) { vWhitelistedRange.push_back(subnet); } -void CNode::copyStats(CNodeStats &stats) +void CNode::copyStats(CNodeStats &stats, const std::vector &m_asmap) { stats.nodeid = this->GetId(); stats.nServices = nServices; + stats.addr = addr; + // stats.addrBind = addrBind; + stats.m_mapped_as = addr.GetMappedAS(m_asmap); stats.nLastSend = nLastSend; stats.nLastRecv = nLastRecv; stats.nTimeConnected = nTimeConnected; @@ -800,8 +803,8 @@ class CompareNetGroupKeyed CSHA256 hashA, hashB; std::vector vchA(32), vchB(32); - vchGroupA = a->addr.GetGroup(); - vchGroupB = b->addr.GetGroup(); + vchGroupA = a->addr.GetGroup(addrman.m_asmap); + vchGroupB = b->addr.GetGroup(addrman.m_asmap); hashA.Write(begin_ptr(vchGroupA), vchGroupA.size()); hashB.Write(begin_ptr(vchGroupB), vchGroupB.size()); @@ -897,14 +900,14 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { int64_t nMostConnectionsTime = 0; std::map, std::vector > mapAddrCounts; BOOST_FOREACH(const CNodeRef &node, vEvictionCandidates) { - mapAddrCounts[node->addr.GetGroup()].push_back(node); - int64_t grouptime = mapAddrCounts[node->addr.GetGroup()][0]->nTimeConnected; - size_t groupsize = mapAddrCounts[node->addr.GetGroup()].size(); + mapAddrCounts[node->addr.GetGroup(addrman.m_asmap)].push_back(node); + int64_t grouptime = mapAddrCounts[node->addr.GetGroup(addrman.m_asmap)][0]->nTimeConnected; + size_t groupsize = mapAddrCounts[node->addr.GetGroup(addrman.m_asmap)].size(); if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) { nMostConnections = groupsize; nMostConnectionsTime = grouptime; - naMostConnections = node->addr.GetGroup(); + naMostConnections = node->addr.GetGroup(addrman.m_asmap); } } @@ -1396,7 +1399,7 @@ void ThreadOpenConnections() static bool done = false; if (!done) { // skip DNS seeds for staked chains. - if ( is_STAKED(ASSETCHAINS_SYMBOL) == 0 && KOMODO_DEX_P2P == 0 ) { + if ( is_STAKED(ASSETCHAINS_SYMBOL) == 0 ) { //LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n"); LogPrintf("Adding fixed seed nodes.\n"); addrman.Add(convertSeed6(Params().FixedSeeds()), CNetAddr("127.0.0.1")); @@ -1419,13 +1422,13 @@ void ThreadOpenConnections() LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { if (!pnode->fInbound) { - setConnected.insert(pnode->addr.GetGroup()); + setConnected.insert(pnode->addr.GetGroup(addrman.m_asmap)); nOutbound++; } } } - int64_t nANow = GetAdjustedTime(); + int64_t nANow = GetTime(); int nTries = 0; while (true) @@ -1433,7 +1436,7 @@ void ThreadOpenConnections() CAddrInfo addr = addrman.Select(); // if we selected an invalid address, restart - if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) + if (!addr.IsValid() || setConnected.count(addr.GetGroup(addrman.m_asmap)) || IsLocal(addr)) break; // If we didn't find an appropriate destination after trying 100 addresses fetched from addrman, @@ -2257,3 +2260,16 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) LEAVE_CRITICAL_SECTION(cs_vSend); } + +void CopyNodeStats(std::vector& vstats) +{ + vstats.clear(); + + LOCK(cs_vNodes); + vstats.reserve(vNodes.size()); + BOOST_FOREACH(CNode* pnode, vNodes) { + CNodeStats stats; + pnode->copyStats(stats, addrman.m_asmap); + vstats.push_back(stats); + } +} diff --git a/src/net.h b/src/net.h index e014bb1db7e..9b841a7f231 100644 --- a/src/net.h +++ b/src/net.h @@ -97,6 +97,9 @@ void SocketSendData(CNode *pnode); typedef int NodeId; +class CNodeStats; +void CopyNodeStats(std::vector& vstats); + struct CombinerAll { typedef bool result_type; @@ -205,6 +208,11 @@ class CNodeStats double dPingTime; double dPingWait; std::string addrLocal; + // Address of this peer + CAddress addr; + // Bind address of our side of the connection + // CAddress addrBind; // https://github.com/bitcoin/bitcoin/commit/a7e3c2814c8e49197889a4679461be42254e5c51 + uint32_t m_mapped_as; }; @@ -276,8 +284,11 @@ class CNode int64_t nLastRecv; int64_t nTimeConnected; int64_t nTimeOffset; - uint32_t prevtimes[16],dexlastping; + uint32_t prevtimes[16]; + // Address of this peer CAddress addr; + // Bind address of our side of the connection + // const CAddress addrBind; // https://github.com/bitcoin/bitcoin/commit/a7e3c2814c8e49197889a4679461be42254e5c51 std::string addrName; CService addrLocal; int nVersion; @@ -420,7 +431,7 @@ class CNode if (addr.IsValid() && !addrKnown.contains(addr.GetKey())) { if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) { vAddrToSend[insecure_rand() % vAddrToSend.size()] = addr; - } else { + } else { vAddrToSend.push_back(addr); } } @@ -643,7 +654,7 @@ class CNode static bool Unban(const CSubNet &ip); static void GetBanned(std::map &banmap); - void copyStats(CNodeStats &stats); + void copyStats(CNodeStats &stats, const std::vector &m_asmap); static bool IsWhitelistedRange(const CNetAddr &ip); static void AddWhitelistedRange(const CSubNet &subnet); diff --git a/src/netbase.cpp b/src/netbase.cpp index fa8b80435ad..3a3faad1c26 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -30,6 +30,7 @@ #include "random.h" #include "util.h" #include "utilstrencodings.h" +#include "crypto/common.h" // for ReadBE32 #ifdef __APPLE__ #ifdef HAVE_GETADDRINFO_A @@ -65,6 +66,9 @@ bool fNameLookup = false; static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; +// 0xFD + sha256("bitcoin")[0:5] +static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 }; + // Need ample time for negotiation for very slow proxies such as Tor (milliseconds) static const int SOCKS5_RECV_TIMEOUT = 20 * 1000; @@ -72,7 +76,7 @@ enum Network ParseNetwork(std::string net) { boost::to_lower(net); if (net == "ipv4") return NET_IPV4; if (net == "ipv6") return NET_IPV6; - if (net == "tor" || net == "onion") return NET_TOR; + if (net == "tor" || net == "onion") return NET_ONION; return NET_UNROUTABLE; } @@ -81,7 +85,7 @@ std::string GetNetworkName(enum Network net) { { case NET_IPV4: return "ipv4"; case NET_IPV6: return "ipv6"; - case NET_TOR: return "onion"; + case NET_ONION: return "onion"; default: return ""; } } @@ -879,6 +883,11 @@ bool CNetAddr::IsRoutable() const return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal()); } +bool CNetAddr::IsInternal() const +{ + return memcmp(ip, g_internal_prefix, sizeof(g_internal_prefix)) == 0; +} + enum Network CNetAddr::GetNetwork() const { if (!IsRoutable()) @@ -888,7 +897,7 @@ enum Network CNetAddr::GetNetwork() const return NET_IPV4; if (IsTor()) - return NET_TOR; + return NET_ONION; return NET_IPV6; } @@ -949,11 +958,88 @@ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const return true; } +bool CNetAddr::HasLinkedIPv4() const +{ + return IsRoutable() && (IsIPv4() || IsRFC6145() || IsRFC6052() || IsRFC3964() || IsRFC4380()); +} + +uint32_t CNetAddr::GetLinkedIPv4() const +{ + if (IsIPv4() || IsRFC6145() || IsRFC6052()) { + // IPv4, mapped IPv4, SIIT translated IPv4: the IPv4 address is the last 4 bytes of the address + return ReadBE32(ip + 12); + } else if (IsRFC3964()) { + // 6to4 tunneled IPv4: the IPv4 address is in bytes 2-6 + return ReadBE32(ip + 2); + } else if (IsRFC4380()) { + // Teredo tunneled IPv4: the IPv4 address is in the last 4 bytes of the address, but bitflipped + return ~ReadBE32(ip + 12); + } + assert(false); +} + +uint32_t CNetAddr::GetNetClass() const { + uint32_t net_class = NET_IPV6; + if (IsLocal()) { + net_class = 255; + } + if (IsInternal()) { + net_class = NET_INTERNAL; + } else if (!IsRoutable()) { + net_class = NET_UNROUTABLE; + } else if (HasLinkedIPv4()) { + net_class = NET_IPV4; + } else if (IsTor()) { + net_class = NET_ONION; + } + return net_class; +} + +uint32_t CNetAddr::GetMappedAS(const std::vector &asmap) const { + uint32_t net_class = GetNetClass(); + if (asmap.size() == 0 || (net_class != NET_IPV4 && net_class != NET_IPV6)) { + return 0; // Indicates not found, safe because AS0 is reserved per RFC7607. + } + std::vector ip_bits(128); + if (HasLinkedIPv4()) { + // For lookup, treat as if it was just an IPv4 address (pchIPv4 prefix + IPv4 bits) + for (int8_t byte_i = 0; byte_i < 12; ++byte_i) { + for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) { + ip_bits[byte_i * 8 + bit_i] = (pchIPv4[byte_i] >> (7 - bit_i)) & 1; + } + } + uint32_t ipv4 = GetLinkedIPv4(); + for (int i = 0; i < 32; ++i) { + ip_bits[96 + i] = (ipv4 >> (31 - i)) & 1; + } + } else { + // Use all 128 bits of the IPv6 address otherwise + for (int8_t byte_i = 0; byte_i < 16; ++byte_i) { + uint8_t cur_byte = GetByte(15 - byte_i); + for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) { + ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1; + } + } + } + uint32_t mapped_as = Interpret(asmap, ip_bits); + return mapped_as; +} + // get canonical identifier of an address' group // no two connections will be attempted to addresses with the same group -std::vector CNetAddr::GetGroup() const +std::vector CNetAddr::GetGroup(const std::vector &asmap) const { std::vector vchRet; + // If non-empty asmap is supplied and the address is IPv4/IPv6, + // return ASN to be used for bucketing. + uint32_t asn = GetMappedAS(asmap); + if (asn != 0) { // Either asmap was empty, or address has non-asmappable net class (e.g. TOR). + vchRet.push_back(NET_IPV6); // IPv4 and IPv6 with same ASN should be in the same bucket + for (int i = 0; i < 4; i++) { + vchRet.push_back((asn >> (8 * i)) & 0xFF); + } + return vchRet; + } int nClass = NET_IPV6; int nStartByte = 0; int nBits = 16; @@ -994,7 +1080,7 @@ std::vector CNetAddr::GetGroup() const } else if (IsTor()) { - nClass = NET_TOR; + nClass = NET_ONION; nStartByte = 6; nBits = 4; } @@ -1072,11 +1158,11 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const case NET_IPV4: return REACH_IPV4; case NET_IPV6: return fTunnel ? REACH_IPV6_WEAK : REACH_IPV6_STRONG; // only prefer giving our IPv6 address if it's not tunnelled } - case NET_TOR: + case NET_ONION: switch(ourNet) { default: return REACH_DEFAULT; case NET_IPV4: return REACH_IPV4; // Tor users can connect to IPv4 as well - case NET_TOR: return REACH_PRIVATE; + case NET_ONION: return REACH_PRIVATE; } case NET_TEREDO: switch(ourNet) { @@ -1093,7 +1179,7 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const case NET_TEREDO: return REACH_TEREDO; case NET_IPV6: return REACH_IPV6_WEAK; case NET_IPV4: return REACH_IPV4; - case NET_TOR: return REACH_PRIVATE; // either from Tor, or don't care about our address + case NET_ONION: return REACH_PRIVATE; // either from Tor, or don't care about our address } } } @@ -1443,3 +1529,8 @@ bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking) return true; } + +bool SanityCheckASMap(const std::vector& asmap) +{ + return SanityCheckASMap(asmap, 128); // For IP address lookups, the input is 128 bits +} diff --git a/src/netbase.h b/src/netbase.h index 8eb40b6b7f5..89ad56f1bd3 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -26,6 +26,7 @@ #include "compat.h" #include "serialize.h" +#include "util/asmap.h" #include #include @@ -47,7 +48,8 @@ enum Network NET_UNROUTABLE = 0, NET_IPV4, NET_IPV6, - NET_TOR, + NET_ONION, + NET_INTERNAL, NET_MAX, }; @@ -91,6 +93,7 @@ class CNetAddr bool IsTor() const; bool IsLocal() const; bool IsRoutable() const; + bool IsInternal() const; bool IsValid() const; bool IsMulticast() const; enum Network GetNetwork() const; @@ -99,7 +102,19 @@ class CNetAddr unsigned int GetByte(int n) const; uint64_t GetHash() const; bool GetInAddr(struct in_addr* pipv4Addr) const; - std::vector GetGroup() const; + uint32_t GetNetClass() const; + + //! For IPv4, mapped IPv4, SIIT translated IPv4, Teredo, 6to4 tunneled addresses, return the relevant IPv4 address as a uint32. + uint32_t GetLinkedIPv4() const; + //! Whether this address has a linked IPv4 address (see GetLinkedIPv4()). + bool HasLinkedIPv4() const; + + // The AS on the BGP path to the node we use to diversify + // peers in AddrMan bucketing based on the AS infrastructure. + // The ip->AS mapping depends on how asmap is constructed. + uint32_t GetMappedAS(const std::vector &asmap) const; + + std::vector GetGroup(const std::vector &asmap) const; int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) const; CNetAddr(const struct in6_addr& pipv6Addr); @@ -223,4 +238,6 @@ bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking); */ struct timeval MillisToTimeval(int64_t nTimeout); +bool SanityCheckASMap(const std::vector& asmap); + #endif // BITCOIN_NETBASE_H diff --git a/src/pow.cpp b/src/pow.cpp index 3d744327933..17103ef1d7b 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -402,37 +402,33 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead if ( zflags[0] == 0 || zflags[0] == 3 ) { // 15 51 102 162 230 303 380 460 543 627 714 803 892 983 1075 These are the 0.5% per blk numerator constants for W=2 to 16 if denominator is 100. - zawy - if ( ASSETCHAINS_ADAPTIVEPOW <= 3 ) - { - if ( ASSETCHAINS_BLOCKTIME >= 60 && ASSETCHAINS_BLOCKTIME < 100 ) - bnTarget = RT_CST_RST_outer(height,pblock->nTime,bnTarget,ts,ct,1,60,1,10); - else if ( ASSETCHAINS_BLOCKTIME >= 100 ) - bnTarget = RT_CST_RST_outer(height,pblock->nTime,bnTarget,ts,ct,1,100,1,10); - if ( bnTarget < origtarget ) - zawyflag = 2; - } - if ( ASSETCHAINS_ADAPTIVEPOW <= 4 ) - bnTarget = RT_CST_RST_outer(height,pblock->nTime,origtarget,ts,ct,15,100,2,20); + if ( ASSETCHAINS_BLOCKTIME >= 60 && ASSETCHAINS_BLOCKTIME < 100 ) + bnTarget = RT_CST_RST_outer(height,pblock->nTime,bnTarget,ts,ct,1,60,1,10); + else if ( ASSETCHAINS_BLOCKTIME >= 100 ) + bnTarget = RT_CST_RST_outer(height,pblock->nTime,bnTarget,ts,ct,1,100,1,10); if ( bnTarget < origtarget ) zawyflag = 2; else { - if ( ASSETCHAINS_ADAPTIVEPOW <= 5 ) - bnTarget = RT_CST_RST_outer(height,pblock->nTime,origtarget,ts,ct,1,2,3,30); + bnTarget = RT_CST_RST_outer(height,pblock->nTime,origtarget,ts,ct,15,100,2,20); if ( bnTarget < origtarget ) zawyflag = 2; else { - if ( ASSETCHAINS_ADAPTIVEPOW <= 6 ) - bnTarget = RT_CST_RST_outer(height,pblock->nTime,origtarget,ts,ct,7,3,6,40); + bnTarget = RT_CST_RST_outer(height,pblock->nTime,origtarget,ts,ct,1,2,3,30); if ( bnTarget < origtarget ) zawyflag = 2; else { - if ( ASSETCHAINS_ADAPTIVEPOW <= 7 ) - bnTarget = RT_CST_RST_outer(height,pblock->nTime,origtarget,ts,ct,12,7,12,50); + bnTarget = RT_CST_RST_outer(height,pblock->nTime,origtarget,ts,ct,7,3,6,40); if ( bnTarget < origtarget ) zawyflag = 2; + else + { + bnTarget = RT_CST_RST_outer(height,pblock->nTime,origtarget,ts,ct,12,7,12,50); + if ( bnTarget < origtarget ) + zawyflag = 2; + } } } } @@ -442,31 +438,31 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead for (i=0; i<50; i++) if ( zflags[i] == 2 ) break; - if ( i < 10 && ASSETCHAINS_ADAPTIVEPOW <= 3 ) + if ( i < 10 ) { bnTarget = RT_CST_RST_inner(height,pblock->nTime,bnTarget,ts,ct,1,i); if ( bnTarget > origtarget ) bnTarget = origtarget; } - if ( i < 20 && ASSETCHAINS_ADAPTIVEPOW <= 4 ) + if ( i < 20 ) { bnTarget2 = RT_CST_RST_inner(height,pblock->nTime,bnTarget,ts,ct,2,i); if ( bnTarget2 < bnTarget ) bnTarget = bnTarget2; } - if ( i < 30 && ASSETCHAINS_ADAPTIVEPOW <= 5 ) + if ( i < 30 ) { bnTarget3 = RT_CST_RST_inner(height,pblock->nTime,bnTarget,ts,ct,3,i); if ( bnTarget3 < bnTarget ) bnTarget = bnTarget3; } - if ( i < 40 && ASSETCHAINS_ADAPTIVEPOW <= 6 ) + if ( i < 40 ) { bnTarget6 = RT_CST_RST_inner(height,pblock->nTime,bnTarget,ts,ct,6,i); if ( bnTarget6 < bnTarget ) bnTarget = bnTarget6; } - if ( i < 50 && ASSETCHAINS_ADAPTIVEPOW <= 7 ) + if ( i < 50 ) { bnTarget12 = RT_CST_RST_inner(height,pblock->nTime,bnTarget,ts,ct,12,i); if ( bnTarget12 < bnTarget) @@ -930,8 +926,6 @@ CChainPower GetBlockProof(const CBlockIndex& block) if (fNegative || fOverflow || bnWorkTarget == 0) return CChainPower(0); - CBlockHeader header = block.GetBlockHeader(); - return CChainPower(0, bnStakeTarget, (~bnWorkTarget / (bnWorkTarget + 1)) + 1); } diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 7b89d1ab36d..0c20908b0ed 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -47,8 +47,6 @@ extern uint32_t ASSETCHAINS_MAGIC; extern std::string ASSETCHAINS_SELFIMPORT; -extern uint8_t ASSETCHAINS_CCZEROTXFEE[256]; -extern bool GetOpReturnData(const CScript &sig, std::vector &data); // Overwinter transaction version static const int32_t OVERWINTER_TX_VERSION = 3; @@ -719,21 +717,6 @@ class CTransaction return (ASSETCHAINS_SELFIMPORT=="PEGSCC" && vin[0].prevout.n == 10e8); } - bool IsPriorityCC() const - { - std::vector vopret; uint8_t evalcode; - - if (vout.size()<1) return (false); - if (vout[vout.size()-1].scriptPubKey.IsOpReturn()) - { - GetOpReturnData(vout[vout.size()-1].scriptPubKey,vopret); - evalcode=vopret[0]; - if (ASSETCHAINS_CCZEROTXFEE[evalcode]) - return (true); - } - return (false); - } - friend bool operator==(const CTransaction& a, const CTransaction& b) { return a.hash == b.hash; diff --git a/src/protocol.h b/src/protocol.h index 4983877c92f..b7ad483ace2 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -94,8 +94,6 @@ enum { NODE_NSPV = (1 << 30), NODE_ADDRINDEX = (1 << 29), NODE_SPENTINDEX = (1 << 28), - NODE_DEXP2P = (1 << 27), - NODE_DEXP2P_INDEXED = (1 << 26), // Bits 24-31 are reserved for temporary experiments. Just pick a bit that // isn't getting used, or one not being used much, and notify the diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index ebfa3b69b3b..eabfc8ce9cf 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -568,7 +568,7 @@ int main(int argc, char *argv[]) return 1; } try { - ReadConfigFile(mapArgs, mapMultiArgs,1); + ReadConfigFile(mapArgs, mapMultiArgs); } catch (const std::exception& e) { QMessageBox::critical(0, QObject::tr("Bitcoin Core"), QObject::tr("Error: Cannot parse configuration file: %1. Only use key=value syntax.").arg(e.what())); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index ca4cd212f9e..38124042861 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -4,19 +4,19 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** -* Copyright © 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ #include "amount.h" #include "chain.h" @@ -51,7 +51,7 @@ using namespace std; extern int32_t KOMODO_INSYNC; extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); -int32_t komodo_notarized_height(int32_t *prevMoMheightp, uint256 *hashp, uint256 *txidp); +int32_t komodo_notarized_height(int32_t *prevMoMheightp,uint256 *hashp,uint256 *txidp); #include "komodo_defs.h" #include "komodo_structs.h" @@ -70,8 +70,7 @@ double GetDifficultyINTERNAL(const CBlockIndex* blockindex, bool networkDifficul uint32_t bits; if (networkDifficulty) { bits = GetNextWorkRequired(blockindex, nullptr, Params().GetConsensus()); - } - else { + } else { bits = blockindex->nBits; } @@ -130,20 +129,20 @@ static UniValue ValuePoolDesc( UniValue blockheaderToJSON(const CBlockIndex* blockindex) { UniValue result(UniValue::VOBJ); - if (blockindex == 0) + if ( blockindex == 0 ) { result.push_back(Pair("error", "null blockhash")); return(result); } - uint256 notarized_hash, notarized_desttxid; int32_t prevMoMheight, notarized_height; - notarized_height = komodo_notarized_height(&prevMoMheight, ¬arized_hash, ¬arized_desttxid); + uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height; + notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); result.push_back(Pair("last_notarized_height", notarized_height)); result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex())); int confirmations = -1; // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->GetHeight() + 1; - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(), confirmations))); + result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(),confirmations))); result.push_back(Pair("rawconfirmations", confirmations)); result.push_back(Pair("height", blockindex->GetHeight())); result.push_back(Pair("version", blockindex->nVersion)); @@ -155,7 +154,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits))); result.push_back(Pair("difficulty", GetDifficulty(blockindex))); result.push_back(Pair("chainwork", blockindex->chainPower.chainWork.GetHex())); - result.push_back(Pair("segid", (int)komodo_segid(0, blockindex->GetHeight()))); + result.push_back(Pair("segid", (int)komodo_segid(0,blockindex->GetHeight()))); if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); @@ -173,17 +172,16 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) { confirmations = chainActive.Height() - blockindex->GetHeight() + 1; - } - else { + } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block is an orphan"); } - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(), confirmations))); + result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(),confirmations))); result.push_back(Pair("rawconfirmations", confirmations)); result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); result.push_back(Pair("height", blockindex->GetHeight())); result.push_back(Pair("version", block.nVersion)); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("segid", (int)komodo_segid(0, blockindex->GetHeight()))); + result.push_back(Pair("segid", (int)komodo_segid(0,blockindex->GetHeight()))); UniValue deltas(UniValue::VARR); @@ -211,7 +209,7 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) if (spentInfo.addressType == 1) { delta.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString())); } - else if (spentInfo.addressType == 2) { + else if (spentInfo.addressType == 2) { delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); } else { @@ -223,8 +221,7 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) delta.push_back(Pair("prevout", (int)input.prevout.n)); inputs.push_back(delta); - } - else { + } else { throw JSONRPCError(RPC_INTERNAL_ERROR, "Spent information not available"); } @@ -241,12 +238,12 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) UniValue delta(UniValue::VOBJ); if (out.scriptPubKey.IsPayToScriptHash()) { - vector hashBytes(out.scriptPubKey.begin() + 2, out.scriptPubKey.begin() + 22); + vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); delta.push_back(Pair("address", CBitcoinAddress(CScriptID(uint160(hashBytes))).ToString())); } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(out.scriptPubKey.begin() + 3, out.scriptPubKey.begin() + 23); + vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); } else if (out.scriptPubKey.IsPayToPublicKey() || out.scriptPubKey.IsPayToCryptoCondition()) { @@ -291,26 +288,26 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) { UniValue result(UniValue::VOBJ); - uint256 notarized_hash, notarized_desttxid; int32_t prevMoMheight, notarized_height; - notarized_height = komodo_notarized_height(&prevMoMheight, ¬arized_hash, ¬arized_desttxid); + uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height; + notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); result.push_back(Pair("last_notarized_height", notarized_height)); result.push_back(Pair("hash", block.GetHash().GetHex())); int confirmations = -1; // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->GetHeight() + 1; - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(), confirmations))); + result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(),confirmations))); result.push_back(Pair("rawconfirmations", confirmations)); result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); result.push_back(Pair("height", blockindex->GetHeight())); result.push_back(Pair("version", block.nVersion)); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("segid", (int)komodo_segid(0, blockindex->GetHeight()))); + result.push_back(Pair("segid", (int)komodo_segid(0,blockindex->GetHeight()))); result.push_back(Pair("finalsaplingroot", block.hashFinalSaplingRoot.GetHex())); UniValue txs(UniValue::VARR); BOOST_FOREACH(const CTransaction&tx, block.vtx) { - if (txDetails) + if(txDetails) { UniValue objTx(UniValue::VOBJ); TxToJSON(tx, uint256(), objTx); @@ -393,22 +390,22 @@ UniValue getdifficulty(const UniValue& params, bool fHelp, const CPubKey& mypk) return GetNetworkDifficulty(); } -bool NSPV_spentinmempool(uint256 &spenttxid, int32_t &spentvini, uint256 txid, int32_t vout); +bool NSPV_spentinmempool(uint256 &spenttxid,int32_t &spentvini,uint256 txid,int32_t vout); bool NSPV_inmempool(uint256 txid); -bool myIsutxo_spentinmempool(uint256 &spenttxid, int32_t &spentvini, uint256 txid, int32_t vout) +bool myIsutxo_spentinmempool(uint256 &spenttxid,int32_t &spentvini,uint256 txid,int32_t vout) { int32_t vini = 0; - if (KOMODO_NSPV_SUPERLITE) - return(NSPV_spentinmempool(spenttxid, spentvini, txid, vout)); - BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) + if ( KOMODO_NSPV_SUPERLITE ) + return(NSPV_spentinmempool(spenttxid,spentvini,txid,vout)); + BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) { const CTransaction &tx = e.GetTx(); const uint256 &hash = tx.GetHash(); - BOOST_FOREACH(const CTxIn &txin, tx.vin) + BOOST_FOREACH(const CTxIn &txin,tx.vin) { //fprintf(stderr,"%s/v%d ",uint256_str(str,txin.prevout.hash),txin.prevout.n); - if (txin.prevout.n == vout && txin.prevout.hash == txid) + if ( txin.prevout.n == vout && txin.prevout.hash == txid ) { spenttxid = hash; spentvini = vini; @@ -423,15 +420,15 @@ bool myIsutxo_spentinmempool(uint256 &spenttxid, int32_t &spentvini, uint256 txi bool mytxid_inmempool(uint256 txid) { - if (KOMODO_NSPV_SUPERLITE) + if ( KOMODO_NSPV_SUPERLITE ) { - + } - BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) + BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) { const CTransaction &tx = e.GetTx(); const uint256 &hash = tx.GetHash(); - if (txid == hash) + if ( txid == hash ) return(true); } return(false); @@ -543,7 +540,7 @@ UniValue getblockdeltas(const UniValue& params, bool fHelp, const CPubKey& mypk) if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); - if (!ReadBlockFromDisk(block, pblockindex, 1)) + if(!ReadBlockFromDisk(block, pblockindex,1)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); return blockToDeltasJSON(block, pblockindex); @@ -577,7 +574,7 @@ UniValue getblockhashes(const UniValue& params, bool fHelp, const CPubKey& mypk) + HelpExampleCli("getblockhashes", "1231614698 1231024505") + HelpExampleRpc("getblockhashes", "1231614698, 1231024505") + HelpExampleCli("getblockhashes", "1231614698 1231024505 '{\"noOrphans\":false, \"logicalTimes\":true}'") - ); + ); unsigned int high = params[0].get_int(); unsigned int low = params[1].get_int(); @@ -608,14 +605,13 @@ UniValue getblockhashes(const UniValue& params, bool fHelp, const CPubKey& mypk) UniValue result(UniValue::VARR); - for (std::vector >::const_iterator it = blockHashes.begin(); it != blockHashes.end(); it++) { + for (std::vector >::const_iterator it=blockHashes.begin(); it!=blockHashes.end(); it++) { if (fLogicalTS) { UniValue item(UniValue::VOBJ); item.push_back(Pair("blockhash", it->first.GetHex())); item.push_back(Pair("logicalts", (int)it->second)); result.push_back(item); - } - else { + } else { result.push_back(it->first.GetHex()); } } @@ -666,62 +662,62 @@ UniValue getlastsegidstakes(const UniValue& params, bool fHelp, const CPubKey& m + HelpExampleRpc("getlastsegidstakes", "1000") ); - if (ASSETCHAINS_STAKED == 0) + if ( ASSETCHAINS_STAKED == 0 ) throw runtime_error("Only applies to ac_staked chains\n"); LOCK(cs_main); int depth = params[0].get_int(); - if (depth > chainActive.Height()) + if ( depth > chainActive.Height() ) throw runtime_error("Not enough blocks to scan back that far.\n"); - - int32_t segids[64] = { 0 }; + + int32_t segids[64] = {0}; int32_t pow = 0; int32_t notset = 0; - for (int64_t i = chainActive.Height(); i > chainActive.Height() - depth; i--) + for (int64_t i = chainActive.Height(); i > chainActive.Height()-depth; i--) { - int8_t segid = komodo_segid(0, i); + int8_t segid = komodo_segid(0,i); //CBlockIndex* pblockindex = chainActive[i]; - if (segid >= 0) + if ( segid >= 0 ) segids[segid] += 1; - else if (segid == -1) + else if ( segid == -1 ) pow++; else notset++; } - - int8_t posperc = 100 * (depth - pow) / depth; - + + int8_t posperc = 100*(depth-pow)/depth; + UniValue ret(UniValue::VOBJ); UniValue objsegids(UniValue::VOBJ); for (int8_t i = 0; i < 64; i++) { char str[4]; sprintf(str, "%d", i); - objsegids.push_back(Pair(str, segids[i])); + objsegids.push_back(Pair(str,segids[i])); } - ret.push_back(Pair("NotSet", notset)); - ret.push_back(Pair("PoW", pow)); - ret.push_back(Pair("PoSPerc", posperc)); - ret.push_back(Pair("SegIds", objsegids)); + ret.push_back(Pair("NotSet",notset)); + ret.push_back(Pair("PoW",pow)); + ret.push_back(Pair("PoSPerc",posperc)); + ret.push_back(Pair("SegIds",objsegids)); return ret; } /*uint256 _komodo_getblockhash(int32_t nHeight) { -uint256 hash; -LOCK(cs_main); -if ( nHeight >= 0 && nHeight <= chainActive.Height() ) -{ -CBlockIndex* pblockindex = chainActive[nHeight]; -hash = pblockindex->GetBlockHash(); -int32_t i; -for (i=0; i<32; i++) -printf("%02x",((uint8_t *)&hash)[i]); -printf(" blockhash.%d\n",nHeight); -} else memset(&hash,0,sizeof(hash)); -return(hash); + uint256 hash; + LOCK(cs_main); + if ( nHeight >= 0 && nHeight <= chainActive.Height() ) + { + CBlockIndex* pblockindex = chainActive[nHeight]; + hash = pblockindex->GetBlockHash(); + int32_t i; + for (i=0; i<32; i++) + printf("%02x",((uint8_t *)&hash)[i]); + printf(" blockhash.%d\n",nHeight); + } else memset(&hash,0,sizeof(hash)); + return(hash); }*/ UniValue getblockheader(const UniValue& params, bool fHelp, const CPubKey& mypk) @@ -861,10 +857,9 @@ UniValue getblock(const UniValue& params, bool fHelp, const CPubKey& mypk) int verbosity = 1; if (params.size() > 1) { - if (params[1].isNum()) { + if(params[1].isNum()) { verbosity = params[1].get_int(); - } - else { + } else { verbosity = params[1].get_bool() ? 1 : 0; } } @@ -882,7 +877,7 @@ UniValue getblock(const UniValue& params, bool fHelp, const CPubKey& mypk) if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); - if (!ReadBlockFromDisk(block, pblockindex, 1)) + if(!ReadBlockFromDisk(block, pblockindex,1)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); if (verbosity == 0) @@ -937,8 +932,8 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp, const CPubKey& mypk UniValue kvsearch(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue ret(UniValue::VOBJ); uint32_t flags; uint8_t value[IGUANA_MAXSCRIPTSIZE * 8], key[IGUANA_MAXSCRIPTSIZE * 8]; int32_t duration, j, height, valuesize, keylen; uint256 refpubkey; static uint256 zeroes; - if (fHelp || params.size() != 1) + UniValue ret(UniValue::VOBJ); uint32_t flags; uint8_t value[IGUANA_MAXSCRIPTSIZE*8],key[IGUANA_MAXSCRIPTSIZE*8]; int32_t duration,j,height,valuesize,keylen; uint256 refpubkey; static uint256 zeroes; + if (fHelp || params.size() != 1 ) throw runtime_error( "kvsearch key\n" "\nSearch for a key stored via the kvupdate command. This feature is only available for asset chains.\n" @@ -962,79 +957,76 @@ UniValue kvsearch(const UniValue& params, bool fHelp, const CPubKey& mypk) + HelpExampleRpc("kvsearch", "\"examplekey\"") ); LOCK(cs_main); - if ((keylen = (int32_t)strlen(params[0].get_str().c_str())) > 0) + if ( (keylen= (int32_t)strlen(params[0].get_str().c_str())) > 0 ) { - ret.push_back(Pair("coin", (char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL))); + ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL))); ret.push_back(Pair("currentheight", (int64_t)chainActive.LastTip()->GetHeight())); - ret.push_back(Pair("key", params[0].get_str())); - ret.push_back(Pair("keylen", keylen)); - if (keylen < sizeof(key)) + ret.push_back(Pair("key",params[0].get_str())); + ret.push_back(Pair("keylen",keylen)); + if ( keylen < sizeof(key) ) { - memcpy(key, params[0].get_str().c_str(), keylen); - if ((valuesize = komodo_kvsearch(&refpubkey, chainActive.LastTip()->GetHeight(), &flags, &height, value, key, keylen)) >= 0) + memcpy(key,params[0].get_str().c_str(),keylen); + if ( (valuesize= komodo_kvsearch(&refpubkey,chainActive.LastTip()->GetHeight(),&flags,&height,value,key,keylen)) >= 0 ) { std::string val; char *valuestr; val.resize(valuesize); valuestr = (char *)val.data(); - memcpy(valuestr, value, valuesize); - if (memcmp(&zeroes, &refpubkey, sizeof(refpubkey)) != 0) - ret.push_back(Pair("owner", refpubkey.GetHex())); - ret.push_back(Pair("height", height)); + memcpy(valuestr,value,valuesize); + if ( memcmp(&zeroes,&refpubkey,sizeof(refpubkey)) != 0 ) + ret.push_back(Pair("owner",refpubkey.GetHex())); + ret.push_back(Pair("height",height)); duration = ((flags >> 2) + 1) * KOMODO_KVDURATION; - ret.push_back(Pair("expiration", (int64_t)(height + duration))); - ret.push_back(Pair("flags", (int64_t)flags)); - ret.push_back(Pair("value", val)); - ret.push_back(Pair("valuesize", valuesize)); - } - else ret.push_back(Pair("error", (char *)"cant find key")); - } - else ret.push_back(Pair("error", (char *)"key too big")); - } - else ret.push_back(Pair("error", (char *)"null key")); + ret.push_back(Pair("expiration", (int64_t)(height+duration))); + ret.push_back(Pair("flags",(int64_t)flags)); + ret.push_back(Pair("value",val)); + ret.push_back(Pair("valuesize",valuesize)); + } else ret.push_back(Pair("error",(char *)"cant find key")); + } else ret.push_back(Pair("error",(char *)"key too big")); + } else ret.push_back(Pair("error",(char *)"null key")); return ret; } UniValue minerids(const UniValue& params, bool fHelp, const CPubKey& mypk) { - uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); uint8_t minerids[2000], pubkeys[65][33]; int32_t i, j, n, numnotaries, tally[129]; - if (fHelp || params.size() != 1) + uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); uint8_t minerids[2000],pubkeys[65][33]; int32_t i,j,n,numnotaries,tally[129]; + if ( fHelp || params.size() != 1 ) throw runtime_error("minerids needs height\n"); LOCK(cs_main); int32_t height = atoi(params[0].get_str().c_str()); - if (height <= 0) + if ( height <= 0 ) height = chainActive.LastTip()->GetHeight(); else { CBlockIndex *pblockindex = chainActive[height]; - if (pblockindex != 0) + if ( pblockindex != 0 ) timestamp = pblockindex->GetBlockTime(); } - if ((n = komodo_minerids(minerids, height, (int32_t)(sizeof(minerids) / sizeof(*minerids)))) > 0) + if ( (n= komodo_minerids(minerids,height,(int32_t)(sizeof(minerids)/sizeof(*minerids)))) > 0 ) { - memset(tally, 0, sizeof(tally)); - numnotaries = komodo_notaries(pubkeys, height, timestamp); - if (numnotaries > 0) + memset(tally,0,sizeof(tally)); + numnotaries = komodo_notaries(pubkeys,height,timestamp); + if ( numnotaries > 0 ) { - for (i = 0; i= numnotaries) + if ( minerids[i] >= numnotaries ) tally[128]++; else tally[minerids[i]]++; } - for (i = 0; i<64; i++) + for (i=0; i<64; i++) { - UniValue item(UniValue::VOBJ); std::string hex, kmdaddress; char *hexstr, kmdaddr[64], *ptr; int32_t m; + UniValue item(UniValue::VOBJ); std::string hex,kmdaddress; char *hexstr,kmdaddr[64],*ptr; int32_t m; hex.resize(66); hexstr = (char *)hex.data(); - for (j = 0; j<33; j++) - sprintf(&hexstr[j * 2], "%02x", pubkeys[i][j]); + for (j=0; j<33; j++) + sprintf(&hexstr[j*2],"%02x",pubkeys[i][j]); item.push_back(Pair("notaryid", i)); - bitcoin_address(kmdaddr, 60, pubkeys[i], 33); + bitcoin_address(kmdaddr,60,pubkeys[i],33); m = (int32_t)strlen(kmdaddr); kmdaddress.resize(m); ptr = (char *)kmdaddress.data(); - memcpy(ptr, kmdaddr, m); + memcpy(ptr,kmdaddr,m); item.push_back(Pair("KMDaddress", kmdaddress)); item.push_back(Pair("pubkey", hex)); @@ -1048,56 +1040,55 @@ UniValue minerids(const UniValue& params, bool fHelp, const CPubKey& mypk) } ret.push_back(Pair("mined", a)); ret.push_back(Pair("numnotaries", numnotaries)); - } - else ret.push_back(Pair("error", (char *)"couldnt extract minerids")); + } else ret.push_back(Pair("error", (char *)"couldnt extract minerids")); return ret; } UniValue notaries(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue a(UniValue::VARR); uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); int32_t i, j, n, m; char *hexstr; uint8_t pubkeys[64][33]; char btcaddr[64], kmdaddr[64], *ptr; - if (fHelp || (params.size() != 1 && params.size() != 2)) + UniValue a(UniValue::VARR); uint32_t timestamp=0; UniValue ret(UniValue::VOBJ); int32_t i,j,n,m; char *hexstr; uint8_t pubkeys[64][33]; char btcaddr[64],kmdaddr[64],*ptr; + if ( fHelp || (params.size() != 1 && params.size() != 2) ) throw runtime_error("notaries height timestamp\n"); LOCK(cs_main); int32_t height = atoi(params[0].get_str().c_str()); - if (params.size() == 2) + if ( params.size() == 2 ) timestamp = (uint32_t)atol(params[1].get_str().c_str()); else timestamp = (uint32_t)time(NULL); - if (height < 0) + if ( height < 0 ) { height = chainActive.LastTip()->GetHeight(); timestamp = chainActive.LastTip()->GetBlockTime(); } - else if (params.size() < 2) + else if ( params.size() < 2 ) { CBlockIndex *pblockindex = chainActive[height]; - if (pblockindex != 0) + if ( pblockindex != 0 ) timestamp = pblockindex->GetBlockTime(); } - if ((n = komodo_notaries(pubkeys, height, timestamp)) > 0) + if ( (n= komodo_notaries(pubkeys,height,timestamp)) > 0 ) { - for (i = 0; i 0) + if ( (opretlen= komodo_pending_withdraws(opretbuf)) > 0 ) ret.push_back(Pair("withdraws", opretbuf)); else ret.push_back(Pair("withdraws", (char *)"")); - for (baseid = 0; baseid<32; baseid++) + for (baseid=0; baseid<32; baseid++) { UniValue item(UniValue::VOBJ); UniValue obj(UniValue::VOBJ); - if (pax_fiatstatus(&available, &deposited, &issued, &withdrawn, &approved, &redeemed, CURRENCIES[baseid]) == 0) + if ( pax_fiatstatus(&available,&deposited,&issued,&withdrawn,&approved,&redeemed,CURRENCIES[baseid]) == 0 ) { - if (deposited != 0 || issued != 0 || withdrawn != 0 || approved != 0 || redeemed != 0) + if ( deposited != 0 || issued != 0 || withdrawn != 0 || approved != 0 || redeemed != 0 ) { item.push_back(Pair("available", ValueFromAmount(available))); item.push_back(Pair("deposited", ValueFromAmount(deposited))); @@ -1135,7 +1126,7 @@ UniValue paxpending(const UniValue& params, bool fHelp, const CPubKey& mypk) item.push_back(Pair("withdrawn", ValueFromAmount(withdrawn))); item.push_back(Pair("approved", ValueFromAmount(approved))); item.push_back(Pair("redeemed", ValueFromAmount(redeemed))); - obj.push_back(Pair(CURRENCIES[baseid], item)); + obj.push_back(Pair(CURRENCIES[baseid],item)); a.push_back(obj); } } @@ -1146,44 +1137,354 @@ UniValue paxpending(const UniValue& params, bool fHelp, const CPubKey& mypk) UniValue paxprice(const UniValue& params, bool fHelp, const CPubKey& mypk) { - if (fHelp || params.size() > 4 || params.size() < 2) + if ( fHelp || params.size() > 4 || params.size() < 2 ) throw runtime_error("paxprice \"base\" \"rel\" height\n"); LOCK(cs_main); - UniValue ret(UniValue::VOBJ); uint64_t basevolume = 0, relvolume, seed; + UniValue ret(UniValue::VOBJ); uint64_t basevolume=0,relvolume,seed; std::string base = params[0].get_str(); std::string rel = params[1].get_str(); int32_t height; - if (params.size() == 2) + if ( params.size() == 2 ) height = chainActive.LastTip()->GetHeight(); else height = atoi(params[2].get_str().c_str()); //if ( params.size() == 3 || (basevolume= COIN * atof(params[3].get_str().c_str())) == 0 ) - basevolume = 100000; - relvolume = komodo_paxprice(&seed, height, (char *)base.c_str(), (char *)rel.c_str(), basevolume); + basevolume = 100000; + relvolume = komodo_paxprice(&seed,height,(char *)base.c_str(),(char *)rel.c_str(),basevolume); ret.push_back(Pair("base", base)); ret.push_back(Pair("rel", rel)); ret.push_back(Pair("height", height)); char seedstr[32]; - sprintf(seedstr, "%llu", (long long)seed); + sprintf(seedstr,"%llu",(long long)seed); ret.push_back(Pair("seed", seedstr)); - if (height < 0 || height > chainActive.Height()) + if ( height < 0 || height > chainActive.Height() ) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); else { CBlockIndex *pblockindex = chainActive[height]; - if (pblockindex != 0) + if ( pblockindex != 0 ) ret.push_back(Pair("timestamp", (int64_t)pblockindex->nTime)); - if (basevolume != 0 && relvolume != 0) + if ( basevolume != 0 && relvolume != 0 ) + { + ret.push_back(Pair("price",((double)relvolume / (double)basevolume))); + ret.push_back(Pair("invprice",((double)basevolume / (double)relvolume))); + ret.push_back(Pair("basevolume",ValueFromAmount(basevolume))); + ret.push_back(Pair("relvolume",ValueFromAmount(relvolume))); + } else ret.push_back(Pair("error", "overflow or error in one or more of parameters")); + } + return ret; +} +// fills pricedata with raw price, correlated and smoothed values for numblock +/*int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind) +{ + int32_t height,i,n,width,numpricefeeds = -1; uint64_t seed,ignore,rngval; uint32_t rawprices[1440*6],*ptr; int64_t *tmpbuf; + width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; // need 2*PRICES_DAYWINDOW previous raw price points to calc PRICES_DAYWINDOW correlated points to calc, in turn, smoothed point + komodo_heightpricebits(&seed,rawprices,firstheight + numblocks - 1); + if ( firstheight < width ) + return(-1); + for (i=0; i2; i++,ht--) + { + if ( ht < 0 || ht > chainActive.Height() ) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); + else { - ret.push_back(Pair("price", ((double)relvolume / (double)basevolume))); - ret.push_back(Pair("invprice", ((double)basevolume / (double)relvolume))); - ret.push_back(Pair("basevolume", ValueFromAmount(basevolume))); - ret.push_back(Pair("relvolume", ValueFromAmount(relvolume))); + if ( (n= komodo_heightpricebits(0,rawprices,ht)) > 0 ) + { + if ( n != numpricefeeds ) + throw JSONRPCError(RPC_INVALID_PARAMETER, "numprices != first numprices"); + else + { + for (j=0; j= width ) + { + for (i=0; i= 0 ) + { + if ( checkprices[1] != correlated[i] ) + { + //fprintf(stderr,"ind.%d ht.%d %.8f != %.8f\n",j,nextheight-1-i,(double)checkprices[1]/COIN,(double)correlated[i]/COIN); + correlated[i] = checkprices[1]; + } + } + } + } + tmpbuf = (int64_t *)calloc(sizeof(int64_t),2*PRICES_DAYWINDOW); + for (i=0; i= 0 ) + { + if ( checkprices[2] != smoothed ) + { + fprintf(stderr,"ind.%d ht.%d %.8f != %.8f\n",j,nextheight-1-i,(double)checkprices[2]/COIN,(double)smoothed/COIN); + smoothed = checkprices[2]; + } + } + UniValue parr(UniValue::VARR); + parr.push_back(ValueFromAmount((int64_t)prices[offset] * komodo_pricemult(j))); + parr.push_back(ValueFromAmount(correlated[i])); + parr.push_back(ValueFromAmount(smoothed)); + // compare to alternate method + p.push_back(parr); + } + free(tmpbuf); + } + else + { + for (i=0; i vexpr; + SplitStr(sexpr, vexpr); + + // debug print parsed strings: + std::cerr << "parsed synthetic: "; + for (auto s : vexpr) + std::cerr << s << " "; + std::cerr << std::endl; + + return PricesBet(txfee, amount, leverage, vexpr); +} + +// pricesaddfunding rpc implementation +UniValue pricesaddfunding(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + if (fHelp || params.size() != 2) + throw runtime_error("pricesaddfunding bettxid amount\n" + "where amount is in coins\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + CAmount txfee = 10000; + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + CAmount amount = atof(params[1].get_str().c_str()) * COIN; + if (amount <= 0) + throw runtime_error("invalid amount\n"); + + return PricesAddFunding(txfee, bettxid, amount); +} + +// rpc pricessetcostbasis implementation +UniValue pricessetcostbasis(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + if (fHelp || params.size() != 1) + throw runtime_error("pricessetcostbasis bettxid\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int64_t txfee = 10000; + + return PricesSetcostbasis(txfee, bettxid); +} + +// pricescashout rpc implementation +UniValue pricescashout(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + if (fHelp || params.size() != 1) + throw runtime_error("pricescashout bettxid\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int64_t txfee = 10000; + + return PricesCashout(txfee, bettxid); +} + +// pricesrekt rpc implementation +UniValue pricesrekt(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + if (fHelp || params.size() != 2) + throw runtime_error("pricesrekt bettxid height\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int32_t height = atoi(params[0].get_str().c_str()); + + int64_t txfee = 10000; + + return PricesRekt(txfee, bettxid, height); +} + +// pricesrekt rpc implementation +UniValue pricesgetorderbook(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + if (fHelp || params.size() != 0) + throw runtime_error("pricesgetorderbook\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + return PricesGetOrderbook(); +} + +// pricesrekt rpc implementation +UniValue pricesrefillfund(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + if (fHelp || params.size() != 1) + throw runtime_error("pricesrefillfund amount\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + CAmount amount = atof(params[0].get_str().c_str()) * COIN; + + return PricesRefillFund(amount); +} + + UniValue gettxout(const UniValue& params, bool fHelp, const CPubKey& mypk) { if (fHelp || params.size() < 2 || params.size() > 3) @@ -1241,12 +1542,11 @@ UniValue gettxout(const UniValue& params, bool fHelp, const CPubKey& mypk) if (!view.GetCoins(hash, coins)) return NullUniValue; mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool - } - else { + } else { if (!pcoinsTip->GetCoins(hash, coins)) return NullUniValue; } - if (n<0 || (unsigned int)n >= coins.vout.size() || coins.vout[n].IsNull()) + if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) return NullUniValue; BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); @@ -1255,14 +1555,13 @@ UniValue gettxout(const UniValue& params, bool fHelp, const CPubKey& mypk) if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) { ret.push_back(Pair("confirmations", 0)); ret.push_back(Pair("rawconfirmations", 0)); - } - else { - ret.push_back(Pair("confirmations", komodo_dpowconfs(coins.nHeight, pindex->GetHeight() - coins.nHeight + 1))); + } else { + ret.push_back(Pair("confirmations", komodo_dpowconfs(coins.nHeight,pindex->GetHeight() - coins.nHeight + 1))); ret.push_back(Pair("rawconfirmations", pindex->GetHeight() - coins.nHeight + 1)); } ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); uint64_t interest; int32_t txheight; uint32_t locktime; - if ((interest = komodo_accrued_interest(&txheight, &locktime, hash, n, coins.nHeight, coins.vout[n].nValue, (int32_t)pindex->GetHeight())) != 0) + if ( (interest= komodo_accrued_interest(&txheight,&locktime,hash,n,coins.nHeight,coins.vout[n].nValue,(int32_t)pindex->GetHeight())) != 0 ) ret.push_back(Pair("interest", ValueFromAmount(interest))); UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); @@ -1338,9 +1637,9 @@ static UniValue NetworkUpgradeDesc(const Consensus::Params& consensusParams, Con rv.push_back(Pair("name", upgrade.strName)); rv.push_back(Pair("activationheight", consensusParams.vUpgrades[idx].nActivationHeight)); switch (NetworkUpgradeState(height, consensusParams, idx)) { - case UPGRADE_DISABLED: rv.push_back(Pair("status", "disabled")); break; - case UPGRADE_PENDING: rv.push_back(Pair("status", "pending")); break; - case UPGRADE_ACTIVE: rv.push_back(Pair("status", "active")); break; + case UPGRADE_DISABLED: rv.push_back(Pair("status", "disabled")); break; + case UPGRADE_PENDING: rv.push_back(Pair("status", "pending")); break; + case UPGRADE_ACTIVE: rv.push_back(Pair("status", "active")); break; } rv.push_back(Pair("info", upgrade.strInfo)); return rv; @@ -1413,45 +1712,43 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp, const CPubKey& my LOCK(cs_main); double progress; - if (ASSETCHAINS_SYMBOL[0] == 0) { + if ( ASSETCHAINS_SYMBOL[0] == 0 ) { progress = Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.LastTip()); - } - else { + } else { int32_t longestchain = KOMODO_LONGESTCHAIN;//komodo_longestchain(); - progress = (longestchain > 0) ? (double)chainActive.Height() / longestchain : 1.0; + progress = (longestchain > 0 ) ? (double) chainActive.Height() / longestchain : 1.0; } UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("chain", Params().NetworkIDString())); - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("synced", KOMODO_INSYNC != 0)); - obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->GetHeight() : -1)); - obj.push_back(Pair("bestblockhash", chainActive.LastTip()->GetBlockHash().GetHex())); - obj.push_back(Pair("difficulty", (double)GetNetworkDifficulty())); - obj.push_back(Pair("verificationprogress", progress)); - obj.push_back(Pair("chainwork", chainActive.LastTip()->chainPower.chainWork.GetHex())); + obj.push_back(Pair("chain", Params().NetworkIDString())); + obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("synced", KOMODO_INSYNC!=0)); + obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->GetHeight() : -1)); + obj.push_back(Pair("bestblockhash", chainActive.LastTip()->GetBlockHash().GetHex())); + obj.push_back(Pair("difficulty", (double)GetNetworkDifficulty())); + obj.push_back(Pair("verificationprogress", progress)); + obj.push_back(Pair("chainwork", chainActive.LastTip()->chainPower.chainWork.GetHex())); if (ASSETCHAINS_LWMAPOS) { - obj.push_back(Pair("chainstake", chainActive.LastTip()->chainPower.chainStake.GetHex())); + obj.push_back(Pair("chainstake", chainActive.LastTip()->chainPower.chainStake.GetHex())); } - obj.push_back(Pair("pruned", fPruneMode)); + obj.push_back(Pair("pruned", fPruneMode)); + + SproutMerkleTree tree; + pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), tree); + obj.push_back(Pair("commitments", static_cast(tree.size()))); + CBlockIndex* tip = chainActive.LastTip(); - if ( KOMODO_NSPV_SUPERLITE == 0 ) - { - SproutMerkleTree tree; - pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), tree); - obj.push_back(Pair("commitments", static_cast(tree.size()))); - - UniValue valuePools(UniValue::VARR); - valuePools.push_back(ValuePoolDesc("sprout", tip->nChainSproutValue, boost::none)); - valuePools.push_back(ValuePoolDesc("sapling", tip->nChainSaplingValue, boost::none)); - obj.push_back(Pair("valuePools", valuePools)); - } + UniValue valuePools(UniValue::VARR); + valuePools.push_back(ValuePoolDesc("sprout", tip->nChainSproutValue, boost::none)); + valuePools.push_back(ValuePoolDesc("sapling", tip->nChainSaplingValue, boost::none)); + obj.push_back(Pair("valuePools", valuePools)); + const Consensus::Params& consensusParams = Params().GetConsensus(); UniValue softforks(UniValue::VARR); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); - obj.push_back(Pair("softforks", softforks)); + obj.push_back(Pair("softforks", softforks)); UniValue upgrades(UniValue::VOBJ); for (int i = Consensus::UPGRADE_OVERWINTER; i < Consensus::MAX_NETWORK_UPGRADES; i++) { @@ -1470,7 +1767,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp, const CPubKey& my while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) block = block->pprev; - obj.push_back(Pair("pruneheight", block->GetHeight())); + obj.push_back(Pair("pruneheight", block->GetHeight())); } return obj; } @@ -1481,10 +1778,10 @@ struct CompareBlocksByHeight bool operator()(const CBlockIndex* a, const CBlockIndex* b) const { /* Make sure that unequal blocks with the same height do not compare - equal. Use the pointers themselves to make a distinction. */ + equal. Use the pointers themselves to make a distinction. */ if (a->GetHeight() != b->GetHeight()) - return (a->GetHeight() > b->GetHeight()); + return (a->GetHeight() > b->GetHeight()); return a < b; } @@ -1528,13 +1825,13 @@ UniValue getchaintips(const UniValue& params, bool fHelp, const CPubKey& mypk) LOCK(cs_main); /* Build up a list of chain tips. We start with the list of all - known blocks, and successively remove blocks that appear as pprev - of another block. */ + known blocks, and successively remove blocks that appear as pprev + of another block. */ /*static pthread_mutex_t mutex; static int32_t didinit; if ( didinit == 0 ) { - pthread_mutex_init(&mutex,NULL); - didinit = 1; + pthread_mutex_init(&mutex,NULL); + didinit = 1; } pthread_mutex_lock(&mutex);*/ std::set setTips; @@ -1544,18 +1841,18 @@ UniValue getchaintips(const UniValue& params, bool fHelp, const CPubKey& mypk) n++; setTips.insert(item.second); } - fprintf(stderr, "iterations getchaintips %d\n", n); + fprintf(stderr,"iterations getchaintips %d\n",n); n = 0; BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) { - const CBlockIndex* pprev = 0; + const CBlockIndex* pprev=0; n++; - if (item.second != 0) + if ( item.second != 0 ) pprev = item.second->pprev; if (pprev) setTips.erase(pprev); } - fprintf(stderr, "iterations getchaintips %d\n", n); + fprintf(stderr,"iterations getchaintips %d\n",n); //pthread_mutex_unlock(&mutex); // Always report the currently active tip. @@ -1564,45 +1861,40 @@ UniValue getchaintips(const UniValue& params, bool fHelp, const CPubKey& mypk) /* Construct the output array. */ UniValue res(UniValue::VARR); const CBlockIndex *forked; BOOST_FOREACH(const CBlockIndex* block, setTips) - { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("height", block->GetHeight())); - obj.push_back(Pair("hash", block->phashBlock->GetHex())); - forked = chainActive.FindFork(block); - if (forked != 0) { - const int branchLen = block->GetHeight() - forked->GetHeight(); - obj.push_back(Pair("branchlen", branchLen)); - - string status; - if (chainActive.Contains(block)) { - // This block is part of the currently active chain. - status = "active"; - } - else if (block->nStatus & BLOCK_FAILED_MASK) { - // This block or one of its ancestors is invalid. - status = "invalid"; - } - else if (block->nChainTx == 0) { - // This block cannot be connected because full block data for it or one of its parents is missing. - status = "headers-only"; - } - else if (block->IsValid(BLOCK_VALID_SCRIPTS)) { - // This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized. - status = "valid-fork"; - } - else if (block->IsValid(BLOCK_VALID_TREE)) { - // The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain. - status = "valid-headers"; - } - else { - // No clue. - status = "unknown"; + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("height", block->GetHeight())); + obj.push_back(Pair("hash", block->phashBlock->GetHex())); + forked = chainActive.FindFork(block); + if ( forked != 0 ) + { + const int branchLen = block->GetHeight() - forked->GetHeight(); + obj.push_back(Pair("branchlen", branchLen)); + + string status; + if (chainActive.Contains(block)) { + // This block is part of the currently active chain. + status = "active"; + } else if (block->nStatus & BLOCK_FAILED_MASK) { + // This block or one of its ancestors is invalid. + status = "invalid"; + } else if (block->nChainTx == 0) { + // This block cannot be connected because full block data for it or one of its parents is missing. + status = "headers-only"; + } else if (block->IsValid(BLOCK_VALID_SCRIPTS)) { + // This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized. + status = "valid-fork"; + } else if (block->IsValid(BLOCK_VALID_TREE)) { + // The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain. + status = "valid-headers"; + } else { + // No clue. + status = "unknown"; + } + obj.push_back(Pair("status", status)); } - obj.push_back(Pair("status", status)); + res.push_back(obj); } - res.push_back(obj); - } return res; } @@ -1610,14 +1902,14 @@ UniValue getchaintips(const UniValue& params, bool fHelp, const CPubKey& mypk) UniValue mempoolInfoToJSON() { UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("size", (int64_t)mempool.size())); - ret.push_back(Pair("bytes", (int64_t)mempool.GetTotalTxSize())); - ret.push_back(Pair("usage", (int64_t)mempool.DynamicMemoryUsage())); + ret.push_back(Pair("size", (int64_t) mempool.size())); + ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); + ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage())); if (Params().NetworkIDString() == "regtest") { ret.push_back(Pair("fullyNotified", mempool.IsFullyNotified())); } - + return ret; } @@ -1652,12 +1944,12 @@ UniValue getchaintxstats(const UniValue& params, bool fHelp, const CPubKey& mypk { if (fHelp || params.size() > 2) throw runtime_error( - "getchaintxstats\n" - "\nCompute statistics about the total number and rate of transactions in the chain.\n" - "\nArguments:\n" - "1. nblocks (numeric, optional) Number of blocks in averaging window.\n" - "2. blockhash (string, optional) The hash of the block which ends the window.\n" - "\nResult:\n" + "getchaintxstats\n" + "\nCompute statistics about the total number and rate of transactions in the chain.\n" + "\nArguments:\n" + "1. nblocks (numeric, optional) Number of blocks in averaging window.\n" + "2. blockhash (string, optional) The hash of the block which ends the window.\n" + "\nResult:\n" "{\n" " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" @@ -1678,8 +1970,7 @@ UniValue getchaintxstats(const UniValue& params, bool fHelp, const CPubKey& mypk if (params[1].isNull()) { LOCK(cs_main); pindex = chainActive.Tip(); - } - else { + } else { uint256 hash(ParseHashV(params[1], "blockhash")); LOCK(cs_main); pindex = LookupBlockIndex(hash); @@ -1695,8 +1986,7 @@ UniValue getchaintxstats(const UniValue& params, bool fHelp, const CPubKey& mypk if (params[0].isNull()) { blockcount = std::max(0, std::min(blockcount, pindex->GetHeight() - 1)); - } - else { + } else { blockcount = params[0].get_int(); if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->GetHeight())) { @@ -1752,7 +2042,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp, const CPubKey& mypk } if (state.IsValid()) { - ActivateBestChain(true, state); + ActivateBestChain(true,state); } if (!state.IsValid()) { @@ -1791,7 +2081,7 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp, const CPubKey& mypk } if (state.IsValid()) { - ActivateBestChain(true, state); + ActivateBestChain(true,state); } if (!state.IsValid()) { @@ -1804,24 +2094,24 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp, const CPubKey& mypk static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // --------------------- ------------------------ ----------------------- ---------- - { "blockchain", "getblockchaininfo", &getblockchaininfo, true }, -{ "blockchain", "getbestblockhash", &getbestblockhash, true }, -{ "blockchain", "getblockcount", &getblockcount, true }, -{ "blockchain", "getblock", &getblock, true }, -{ "blockchain", "getblockhash", &getblockhash, true }, -{ "blockchain", "getblockheader", &getblockheader, true }, -{ "blockchain", "getchaintips", &getchaintips, true }, -{ "blockchain", "getchaintxstats", &getchaintxstats, true }, -{ "blockchain", "getdifficulty", &getdifficulty, true }, -{ "blockchain", "getmempoolinfo", &getmempoolinfo, true }, -{ "blockchain", "getrawmempool", &getrawmempool, true }, -{ "blockchain", "gettxout", &gettxout, true }, -{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, -{ "blockchain", "verifychain", &verifychain, true }, - -/* Not shown in help */ -{ "hidden", "invalidateblock", &invalidateblock, true }, -{ "hidden", "reconsiderblock", &reconsiderblock, true }, + { "blockchain", "getblockchaininfo", &getblockchaininfo, true }, + { "blockchain", "getbestblockhash", &getbestblockhash, true }, + { "blockchain", "getblockcount", &getblockcount, true }, + { "blockchain", "getblock", &getblock, true }, + { "blockchain", "getblockhash", &getblockhash, true }, + { "blockchain", "getblockheader", &getblockheader, true }, + { "blockchain", "getchaintips", &getchaintips, true }, + { "blockchain", "getchaintxstats", &getchaintxstats, true }, + { "blockchain", "getdifficulty", &getdifficulty, true }, + { "blockchain", "getmempoolinfo", &getmempoolinfo, true }, + { "blockchain", "getrawmempool", &getrawmempool, true }, + { "blockchain", "gettxout", &gettxout, true }, + { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, + { "blockchain", "verifychain", &verifychain, true }, + + /* Not shown in help */ + { "hidden", "invalidateblock", &invalidateblock, true }, + { "hidden", "reconsiderblock", &reconsiderblock, true }, }; void RegisterBlockchainRPCCommands(CRPCTable &tableRPC) diff --git a/src/rpc/crosschain.cpp b/src/rpc/crosschain.cpp index 648de7867d4..864b257fd52 100644 --- a/src/rpc/crosschain.cpp +++ b/src/rpc/crosschain.cpp @@ -63,8 +63,6 @@ extern std::string ASSETCHAINS_SELFIMPORT; //std::string MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount, CMutableTransaction &mtx); //int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript &scriptPubKey, TxProof &proof, std::string rawsourcetx, int32_t &ivout, uint256 sourcetxid, uint64_t burnAmount); std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string srcaddr, std::vector vouts); -extern void Lock2NSPV(const CPubKey &pk); -extern void Unlock2NSPV(const CPubKey &pk); UniValue assetchainproof(const UniValue& params, bool fHelp, const CPubKey& mypk) { @@ -287,7 +285,7 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp, const CAmount burnAmount; if(params.size() == 3) - burnAmount = AmountFromValue(params[2].get_str().c_str()); + burnAmount = (CAmount)( atof(params[2].get_str().c_str()) * COIN + 0.00000000499999 ); else burnAmount = atoll(params[2].get_str().c_str()); @@ -300,7 +298,7 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp, const if( params.size() == 4 ) tokenid = Parseuint256(params[3].get_str().c_str()); - if (tokenid != zeroid && strcmp("LABS", targetSymbol.c_str()) == 0) + if ( tokenid != zeroid && strcmp("LABS", targetSymbol.c_str())) throw JSONRPCError(RPC_TYPE_ERROR, "There is no tokens support on LABS."); CPubKey myPubKey = Mypubkey(); @@ -347,7 +345,7 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp, const vscript_t vopretBurnData; std::vector vorigpubkey, vdestpubkey; std::string name, description; - std::vector oprets; + std::vector> oprets; if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) throw runtime_error("Could not load token creation tx\n"); @@ -356,9 +354,9 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp, const if (tokenbasetx.vout.size() == 0) throw runtime_error("No vouts in token tx\n"); - if (DecodeTokenCreateOpRetV1(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, description, oprets) != 'c') + if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, description, oprets) != 'c') throw runtime_error("Incorrect token creation tx\n"); - GetOpReturnCCBlob(oprets, vopretNonfungible); + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); /* allow fungible tokens: if (vopretNonfungible.empty()) throw runtime_error("No non-fungible token data\n"); */ @@ -388,11 +386,11 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp, const mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens, NULL))); // new marker to token cc addr, burnable and validated, vout position now changed to 0 (from 1) mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, burnAmount, destPubKey)); - std::vector opretsnft; + std::vector> voprets; if (!vopretNonfungible.empty()) - opretsnft.push_back(vopretNonfungible); // add additional opret with non-fungible data + voprets.push_back(std::make_pair(OPRETID_NONFUNGIBLEDATA, vopretNonfungible)); // add additional opret with non-fungible data - mtx.vout.push_back(CTxOut((CAmount)0, EncodeTokenCreateOpRetV1(vorigpubkey, name, description, opretsnft))); // make token import opret + mtx.vout.push_back(CTxOut((CAmount)0, EncodeTokenCreateOpRet('c', vorigpubkey, name, description, voprets))); // make token import opret ret.push_back(Pair("payouts", HexStr(E_MARSHAL(ss << mtx.vout)))); // save payouts for import tx rawproof = E_MARSHAL(ss << chainSymbol << tokenbasetx); // add src chain name and token creation tx @@ -415,7 +413,7 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp, const mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, ccChange, myPubKey)); GetOpReturnData(burnOut.scriptPubKey, vopretBurnData); - mtx.vout.push_back(CTxOut(txfee, EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, { vopretBurnData }))); //burn txfee for miners in dest chain + mtx.vout.push_back(CTxOut(txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, std::make_pair(OPRETID_BURNDATA, vopretBurnData)))); //burn txfee for miners in dest chain } std::string burnTxHex = FinalizeCCTx(0, cpTokens, mtx, myPubKey, txfee, CScript()); //no change, no opret @@ -453,9 +451,10 @@ void CheckBurnTxSource(uint256 burntxid, UniValue &info) { if (!E_UNMARSHAL(rawproof, ss >> sourceSymbol; ss >> tokenbasetxStored)) throw std::runtime_error("Cannot unmarshal rawproof for tokens"); + uint8_t evalCode; std::vector voutPubkeys; - std::vector oprets; - if( DecodeTokenOpRetV1(burnTx.vout.back().scriptPubKey, tokenid, voutPubkeys, oprets) == 0 ) + std::vector> oprets; + if( DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCode, tokenid, voutPubkeys, oprets) == 0 ) throw std::runtime_error("Cannot decode token opret in burn tx"); if( tokenid != tokenbasetxStored.GetHash() ) @@ -471,11 +470,11 @@ void CheckBurnTxSource(uint256 burntxid, UniValue &info) { if (tokenbasetx.vout.size() > 0) { std::vector origpubkey; std::string name, description; - std::vector oprets; + std::vector> oprets; vscript_t vopretNonfungible; - if (DecodeTokenCreateOpRetV1(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets) == 'c') { - GetOpReturnCCBlob(oprets, vopretNonfungible); + if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets) == 'c') { + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); if (vopretNonfungible.empty()) throw std::runtime_error("Could not migrate fungible tokens"); } @@ -488,9 +487,8 @@ void CheckBurnTxSource(uint256 burntxid, UniValue &info) { struct CCcontract_info *cpTokens, CCtokens_info; cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS); - //int64_t ccInputs = 0, ccOutputs = 0; - std::string errorStr; - if( !TokensExactAmounts(true, cpTokens, NULL, burnTx, errorStr) ) + int64_t ccInputs = 0, ccOutputs = 0; + if( !TokensExactAmounts(true, cpTokens, ccInputs, ccOutputs, NULL, burnTx, tokenid) ) throw std::runtime_error("Incorrect token burn tx: cc inputs <> cc outputs"); } else if (vopret.begin()[0] == EVAL_IMPORTCOIN) { @@ -733,7 +731,7 @@ UniValue selfimport(const UniValue& params, bool fHelp, const CPubKey& mypk) "\ncreates self import coin transaction"); destaddr = params[0].get_str(); - burnAmount = AmountFromValue(params[1]); + burnAmount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; source = ASSETCHAINS_SELFIMPORT; //defaults to -ac_import=... param @@ -866,13 +864,13 @@ UniValue importgatewaybind(const UniValue& params, bool fHelp, const CPubKey& my { UniValue result(UniValue::VOBJ); CMutableTransaction mtx; std::vector pubkey; - std::string coin; int32_t i,M,N; std::vector pubkeys; + std::string hex,coin; int32_t i,M,N; std::vector pubkeys; uint256 oracletxid; uint8_t p1,p2,p3,p4; if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) throw runtime_error("importgatewaybind only works on -ac_import chains"); - if ( fHelp || params.size() < 8) - throw runtime_error("importgatewaybind coin orcletxid M N pubkeys pubtype p2shtype wiftype [taddr]\n"); + if ( fHelp || params.size() != 8) + throw runtime_error("use \'importgatewaybind coin orcletxid M N pubkeys pubtype p2shtype wiftype [taddr]\' to bind an import gateway\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); CCerror = ""; @@ -905,13 +903,13 @@ UniValue importgatewaybind(const UniValue& params, bool fHelp, const CPubKey& my ERR_RESULT("source coin not equal to ac_import name"); return result; } - Lock2NSPV(mypk); - result = ImportGatewayBind(mypk,0, coin, oracletxid, M, N, pubkeys, p1, p2, p3, p4); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) + hex = ImportGatewayBind(0, coin, oracletxid, M, N, pubkeys, p1, p2, p3, p4); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); - } - Unlock2NSPV(mypk); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaybind"); return result; } @@ -919,13 +917,13 @@ UniValue importgatewaydeposit(const UniValue& params, bool fHelp, const CPubKey& { UniValue result(UniValue::VOBJ); CMutableTransaction mtx; std::vector rawproof; - std::string coin,rawburntx; int32_t height,burnvout; uint64_t amount; + std::string hex,coin,rawburntx; int32_t height,burnvout; int64_t amount; CPubKey destpub; std::vector vouts; uint256 bindtxid,burntxid; if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) throw runtime_error("importgatewaydeposit only works on -ac_import chains"); if ( fHelp || params.size() != 9) - throw runtime_error("importgatewaydeposit bindtxid height coin cointxid markervout rawhex rawproof destpubkey amount\n"); + throw runtime_error("use \'importgatewaydeposit bindtxid height coin burntxid nvout rawburntx rawproof destpub amount\' to import deposited coins\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); CCerror = ""; @@ -948,13 +946,13 @@ UniValue importgatewaydeposit(const UniValue& params, bool fHelp, const CPubKey& ERR_RESULT("source coin not equal to ac_import name"); return result; } - Lock2NSPV(mypk); - result = ImportGatewayDeposit(mypk,0, bindtxid, height, coin, burntxid, burnvout, rawburntx, rawproof, destpub, amount); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) + hex = ImportGatewayDeposit(0, bindtxid, height, coin, burntxid, burnvout, rawburntx, rawproof, destpub, amount); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); - } - Unlock2NSPV(mypk); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaydeposit"); return result; } @@ -962,13 +960,13 @@ UniValue importgatewaywithdraw(const UniValue& params, bool fHelp, const CPubKey { UniValue result(UniValue::VOBJ); CMutableTransaction mtx; std::vector rawproof; - std::string coin,rawburntx; int64_t amount; int32_t height,burnvout; + std::string hex,coin,rawburntx; int64_t amount; int32_t height,burnvout; CPubKey destpub; std::vector vouts; uint256 bindtxid,burntxid; if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) throw runtime_error("importgatewaywithdraw only works on -ac_import chains"); if ( fHelp || params.size() != 4) - throw runtime_error("importgatewaywithdraw bindtxid coin withdrawpubkey amount\n"); + throw runtime_error("use \'importgatewaywithdraw bindtxid coin withdrawpub amount\' to burn imported coins and withdraw them on external chain\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); CCerror = ""; @@ -986,80 +984,103 @@ UniValue importgatewaywithdraw(const UniValue& params, bool fHelp, const CPubKey ERR_RESULT("source coin not equal to ac_import name"); return result; } - Lock2NSPV(mypk); - result = ImportGatewayWithdraw(mypk,0, bindtxid, coin, destpub, amount); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) + hex = ImportGatewayWithdraw(0, bindtxid, coin, destpub, amount); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); - } - Unlock2NSPV(mypk); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaywithdraw"); return result; } -UniValue importgatewaywithdrawsign(const UniValue& params, bool fHelp, const CPubKey& mypk) +UniValue importgatewaypartialsign(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue result(UniValue::VOBJ); uint256 lasttxid; std::string txhex,coin; + UniValue result(UniValue::VOBJ); std::string coin,parthex,hex; uint256 txid; if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) - throw runtime_error("importgatewaywithdrawsign only works on -ac_import chains"); + throw runtime_error("importgatewayspartialsign only works on -ac_import chains"); if ( fHelp || params.size() != 3 ) - throw runtime_error("importgatewaywithdrawsign lasttxid coin hex\n"); + throw runtime_error("importgatewayspartialsign txidaddr refcoin hex\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); - lasttxid = Parseuint256((char *)params[0].get_str().c_str()); + txid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + parthex = params[2].get_str(); + hex = ImportGatewayPartialSign(0,txid,coin,parthex); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex",hex)); + } else ERR_RESULT("couldnt importgatewayspartialsign"); + return(result); +} + +UniValue importgatewaycompletesigning(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); uint256 withdrawtxid; std::string txhex,hex,coin; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewaycompletesigning only works on -ac_import chains"); + if ( fHelp || params.size() != 3 ) + throw runtime_error("importgatewaycompletesigning withdrawtxid coin hex\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + withdrawtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); txhex = params[2].get_str(); - Lock2NSPV(mypk); - result = ImportGatewayWithdrawSign(mypk,0,lasttxid,coin,txhex); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) + hex = ImportGatewayCompleteSigning(0,withdrawtxid,coin,txhex); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); - } - Unlock2NSPV(mypk); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaycompletesigning"); return(result); } UniValue importgatewaymarkdone(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue result(UniValue::VOBJ); uint256 withdrawsigntxid; std::string coin; + UniValue result(UniValue::VOBJ); uint256 completetxid; std::string hex,coin; if ( fHelp || params.size() != 2 ) - throw runtime_error("importgatewaymarkdone withdrawsigntxid coin\n"); + throw runtime_error("importgatewaymarkdone completesigningtx coin\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); - withdrawsigntxid = Parseuint256((char *)params[0].get_str().c_str()); + completetxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); - Lock2NSPV(mypk); - result = ImportGatewayMarkDone(mypk,0,withdrawsigntxid,coin); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) + hex = ImportGatewayMarkDone(0,completetxid,coin); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); - } - Unlock2NSPV(mypk); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaymarkdone"); return(result); } -UniValue importgatewaypendingsignwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk) +UniValue importgatewaypendingwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 bindtxid; std::string coin; if ( fHelp || params.size() != 2 ) - throw runtime_error("importgatewaypendingsignwithdraws bindtxid coin\n"); + throw runtime_error("importgatewaypendingwithdraws bindtxid coin\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); - return(ImportGatewayPendingSignWithdraws(mypk,bindtxid,coin)); + return(ImportGatewayPendingWithdraws(bindtxid,coin)); } -UniValue importgatewaysignedwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk) +UniValue importgatewayprocessed(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 bindtxid; std::string coin; if ( fHelp || params.size() != 2 ) - throw runtime_error("importgatewaysignedwithdraws bindtxid coin\n"); + throw runtime_error("importgatewayprocessed bindtxid coin\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); - return(ImportGatewaySignedWithdraws(mypk,bindtxid,coin)); + return(ImportGatewayProcessedWithdraws(bindtxid,coin)); } UniValue importgatewayexternaladdress(const UniValue& params, bool fHelp, const CPubKey& mypk) @@ -1094,9 +1115,9 @@ UniValue importgatewaydumpprivkey(const UniValue& params, bool fHelp, const CPub throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); } CKey vchSecret; - if (!pwalletMain->GetKey(*keyID, vchSecret)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); - } + // if (!pwalletMain->GetKey(*keyID, vchSecret)) { + // throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); + //} return(ImportGatewayDumpPrivKey(bindtxid,vchSecret)); } @@ -1396,26 +1417,27 @@ UniValue getwalletburntransactions(const UniValue& params, bool fHelp, const CPu if (vopret.begin()[0] == EVAL_TOKENS) { // get burned token value - std::vector oprets; + std::vector> oprets; uint256 tokenid; + uint8_t evalCodeInOpret; std::vector voutTokenPubkeys; //skip token opret: - if (DecodeTokenOpRetV1(pwtx->vout.back().scriptPubKey, tokenid, voutTokenPubkeys, oprets) != 0) { + if (DecodeTokenOpRet(pwtx->vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) != 0) { CTransaction tokenbasetx; uint256 hashBlock; if (myGetTransaction(tokenid, tokenbasetx, hashBlock)) { std::vector vorigpubkey; std::string name, description; - std::vector oprets; + std::vector> oprets; if (tokenbasetx.vout.size() > 0 && - DecodeTokenCreateOpRetV1(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, description, oprets) == 'c') + DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, description, oprets) == 'c') { uint8_t destEvalCode = EVAL_TOKENS; // init set to fungible token: vscript_t vopretNonfungible; - GetOpReturnCCBlob(oprets, vopretNonfungible); + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); if (!vopretNonfungible.empty()) destEvalCode = vopretNonfungible.begin()[0]; diff --git a/src/rpc/marmararpc.cpp b/src/rpc/marmararpc.cpp deleted file mode 100644 index 9e77d789c81..00000000000 --- a/src/rpc/marmararpc.cpp +++ /dev/null @@ -1,623 +0,0 @@ -/****************************************************************************** - * Copyright 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include -#include -#include -#include "univalue.h" -#include "amount.h" -#include "rpc/server.h" -#include "rpc/protocol.h" - -#include "../wallet/crypter.h" -#include "../wallet/rpcwallet.h" - -#include "sync_ext.h" - -#include "cc/CCinclude.h" -#include "cc/CCMarmara.h" - -using namespace std; - -UniValue marmaraaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - struct CCcontract_info *cp, C; std::vector pubkey; - cp = CCinit(&C, EVAL_MARMARA); - if (fHelp || params.size() > 1) - throw runtime_error("Marmaraaddress [pubkey]\n"); - if (ensure_CCrequirements(cp->evalcode) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - if (params.size() == 1) - pubkey = ParseHex(params[0].get_str().c_str()); - return(CCaddress(cp, (char *)"Marmara", pubkey)); -} - -UniValue marmara_poolpayout(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - int32_t firstheight; double perc; char *jsonstr; - if ( fHelp || params.size() != 3 ) - { - // marmarapoolpayout 0.5 2 '[["024131032ed90941e714db8e6dd176fe5a86c9d873d279edecf005c06f773da686",1000],["02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92",100]]'; - //marmarapoolpayout 0 2 '[["024131032ed90941e714db8e6dd176fe5a86c9d873d279edecf005c06f773da686",1000]]' - throw runtime_error("marmarapoolpayout perc firstheight \"[[\\\"pubkey\\\":shares], ...]\"\n"); - } - if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); -#endif - - perc = atof(params[0].get_str().c_str()) / 100.; - firstheight = atol(params[1].get_str().c_str()); - jsonstr = (char *)params[2].get_str().c_str(); - return "not implemented"; - //return(MarmaraPoolPayout(0,firstheight,perc,jsonstr)); // [[pk0, shares0], [pk1, shares1], ...] -} - -UniValue marmara_receive(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ), jsonParams(UniValue::VOBJ); - uint256 batontxid; std::vector senderpub; int64_t amount = 0; int32_t matures = 0; std::string currency; - - if (fHelp || (params.size() != 5 && params.size() != 3)) - { - // automatic flag -> lsb of matures - - throw runtime_error( - "marmarareceive senderpk amount currency matures '{\"avalcount\":\"n\"}'\n" - "marmarareceive senderpk batontxid '{\"avalcount\":\"n\"}'\n" - "creates requesttx for issuer or endorser.\nFor the first call batontxid should be empty.\n" - "the value of 'matures' is relative block number from the current height\n" "\n"); - } - if (ensure_CCrequirements(EVAL_MARMARA) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); -#endif - - memset(&batontxid, 0, sizeof(batontxid)); - senderpub = ParseHex(params[0].get_str().c_str()); - if (senderpub.size() != 33) - { - ERR_RESULT("invalid sender pubkey"); - return result; - } - - int njson; - if (params.size() == 5) - { - amount = AmountFromValue(params[1]); - currency = params[2].get_str(); - matures = chainActive.LastTip()->GetHeight() + atol(params[3].get_str().c_str()) + 1; // if no baton (first call) then matures value is relative - njson = 4; - } - else - { - batontxid = Parseuint256((char *)params[1].get_str().c_str()); - if (batontxid.IsNull()) - throw runtime_error("incorrect batontxid\n"); - njson = 2; - } - - // parse json: - if (params[njson].getType() == UniValue::VOBJ) // as json in {...} - jsonParams = params[njson].get_obj(); - else if (params[njson].getType() == UniValue::VSTR) // as json in quoted string '{...}' - jsonParams.read(params[njson].get_str().c_str()); - if (jsonParams.getType() != UniValue::VOBJ || jsonParams.empty()) - throw runtime_error("last parameter must be object\n"); - std::cerr << __func__ << " test output optParams=" << jsonParams.write(0, 0) << std::endl; - // TODO: check allowed params - int32_t avalcount = 0; - std::vector keys = jsonParams.getKeys(); - std::vector::iterator iter = std::find(keys.begin(), keys.end(), "avalcount"); - if (iter != keys.end()) { - avalcount = atoi(jsonParams[iter - keys.begin()].get_str().c_str()); - //std::cerr << __func__ << " test output avalcount=" << avalcount << std::endl; - } - - result = MarmaraReceive(remotepk, 0, pubkey2pk(senderpub), amount, currency, matures, avalcount, batontxid, true); - return result; -} - -UniValue marmara_issue(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ), jsonParams(UniValue::VOBJ); - uint256 requesttxid; - std::vector receiverpub; - - if (fHelp || params.size() != 3) - { - throw runtime_error("marmaraissue receiverpk '{\"avalcount\":\"n\", \"autosettlement\":\"true\"|\"false\", \"autoinsurance\":\"true\"|\"false\", \"disputeexpires\":\"offset\", \"EscrowOn\":\"true\"|\"false\", \"BlockageAmount\":\"amount\" }' requesttxid\n"); - } - if( ensure_CCrequirements(EVAL_MARMARA) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); -#endif - - receiverpub = ParseHex(params[0].get_str().c_str()); - if (receiverpub.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) - { - ERR_RESULT("invalid receiver pubkey"); - return result; - } - - // parse json params: - if (params[1].getType() == UniValue::VOBJ) - jsonParams = params[1].get_obj(); - else if (params[1].getType() == UniValue::VSTR) // json in quoted string '{...}' - jsonParams.read(params[1].get_str().c_str()); - if (jsonParams.getType() != UniValue::VOBJ || jsonParams.empty()) - throw runtime_error("parameter 2 must be object\n"); - //std::cerr << __func__ << " test output optParams=" << jsonParams.write(0, 0) << std::endl; - - // TODO: check only allowed params present - struct SMarmaraOptParams optParams; - std::vector keys = jsonParams.getKeys(); - std::vector::iterator iter; - - iter = std::find(keys.begin(), keys.end(), "avalcount"); - if (iter != keys.end()) { - optParams.avalCount = atoi(jsonParams[iter - keys.begin()].get_str().c_str()); - //std::cerr << __func__ << " test output avalcount=" << optParams.avalCount << std::endl; - } - iter = std::find(keys.begin(), keys.end(), "autosettlement"); - if (iter != keys.end()) { - std::string value = jsonParams[iter - keys.begin()].get_str(); - optParams.autoSettlement = std::equal(value.begin(), value.end(), "true", [](char c1, char c2) {return std::toupper(c1) == std::toupper(c2);}); - //std::cerr << __func__ << " test output autosettlement=" << optParams.autoSettlement << std::endl; - } - iter = std::find(keys.begin(), keys.end(), "autoinsurance"); - if (iter != keys.end()) { - std::string value = jsonParams[iter - keys.begin()].get_str(); - optParams.autoInsurance = std::equal(value.begin(), value.end(), "true", [](char c1, char c2) {return std::toupper(c1) == std::toupper(c2);}); - //std::cerr << __func__ << " test output autoinsurance=" << optParams.autoInsurance << std::endl; - } - iter = std::find(keys.begin(), keys.end(), "disputeexpires"); - if (iter != keys.end()) { - std::string value = jsonParams[iter - keys.begin()].get_str(); - optParams.disputeExpiresOffset = atoi(jsonParams[iter - keys.begin()].get_str().c_str()); - //std::cerr << __func__ << " test output disputeexpiresoffset=" << optParams.disputeExpiresOffset << std::endl; - } - iter = std::find(keys.begin(), keys.end(), "EscrowOn"); - if (iter != keys.end()) { - std::string value = jsonParams[iter - keys.begin()].get_str(); - optParams.escrowOn = std::equal(value.begin(), value.end(), "true", [](char c1, char c2) {return std::toupper(c1) == std::toupper(c2);}); - //std::cerr << __func__ << " test output EscrowOn=" << optParams.escrowOn << std::endl; - } - iter = std::find(keys.begin(), keys.end(), "BlockageAmount"); - if (iter != keys.end()) { - optParams.blockageAmount = atoll(jsonParams[iter - keys.begin()].get_str().c_str()); - //std::cerr << __func__ << " test output BlockageAmount=" << optParams.blockageAmount << std::endl; - } - - requesttxid = Parseuint256((char *)params[2].get_str().c_str()); - if (requesttxid.IsNull()) - throw runtime_error("incorrect requesttxid\n"); - - result = MarmaraIssue(remotepk, 0, MARMARA_ISSUE, pubkey2pk(receiverpub), optParams, requesttxid, zeroid); - return result; -} - -UniValue marmara_transfer(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ), jsonParams(UniValue::VOBJ); - uint256 requesttxid, batontxid; - std::vector receiverpub; - std::vector creditloop; - - if (fHelp || params.size() != 3) - { - throw runtime_error("marmaratransfer receiverpk '{\"avalcount\":\"n\"}' requesttxid\n"); - } - if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - receiverpub = ParseHex(params[0].get_str().c_str()); - if (receiverpub.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) - { - ERR_RESULT("invalid receiver pubkey"); - return result; - } - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); -#endif - - // parse json params: - if (params[1].getType() == UniValue::VOBJ) - jsonParams = params[1].get_obj(); - else if (params[1].getType() == UniValue::VSTR) // json in quoted string '{...}' - jsonParams.read(params[1].get_str().c_str()); - if (jsonParams.getType() != UniValue::VOBJ || jsonParams.empty()) - throw runtime_error("parameter 2 must be object\n"); - //std::cerr << __func__ << " test output optParams=" << jsonParams.write(0, 0) << std::endl; - // TODO: check only allowed params present - struct SMarmaraOptParams optParams; - std::vector keys = jsonParams.getKeys(); - std::vector::iterator iter; - - iter = std::find(keys.begin(), keys.end(), "avalcount"); - if (iter != keys.end()) { - optParams.avalCount = atoi(jsonParams[iter - keys.begin()].get_str().c_str()); - //std::cerr << __func__ << " test output avalcount=" << optParams.avalCount << std::endl; - } - - requesttxid = Parseuint256((char *)params[2].get_str().c_str()); - if (requesttxid.IsNull()) - throw runtime_error("incorrect requesttxid\n"); - - // find the baton for transfer call: - if (MarmaraGetbatontxid(creditloop, batontxid, requesttxid) < 0) - throw runtime_error("couldnt find batontxid\n"); - - result = MarmaraIssue(remotepk, 0, MARMARA_TRANSFER, pubkey2pk(receiverpub), optParams, requesttxid, batontxid); - return result; -} - -UniValue marmara_info(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - CPubKey pk; - std::vector vpk; - int64_t minamount,maxamount; - int32_t firstheight,lastheight; - std::string currency; - - if ( fHelp || params.size() < 4 || params.size() > 6 ) - { - throw runtime_error("marmarainfo firstheight lastheight minamount maxamount [pk currency]\n"); - } - if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - /* - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - */ - firstheight = atol(params[0].get_str().c_str()); - lastheight = atol(params[1].get_str().c_str()); - minamount = atof(params[2].get_str().c_str()) * COIN; - maxamount = atof(params[3].get_str().c_str()) * COIN; - if (params.size() >= 5) { - vpk = ParseHex(params[4].get_str().c_str()); - if (vpk.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) - { - ERR_RESULT("invalid pubkey parameter"); - return result; - } - pk = pubkey2pk(vpk); - } - if ( params.size() == 6 ) - { - currency = params[5].get_str(); - } - - /*if (!pk.IsValid()) { - // if pk param not set then use mypk or remote pk - if (remotepk.IsValid()) - pk = remotepk; - else - pk = pubkey2pk(Mypubkey()); - }*/ - - result = MarmaraInfo(pk, firstheight, lastheight, minamount, maxamount, currency); - return(result); -} - -UniValue marmara_creditloop(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); uint256 txid; - if ( fHelp || params.size() != 1 ) - { - // marmaracreditloop 010ff7f9256cefe3b5dee3d72c0eeae9fc6f34884e6f32ffe5b60916df54a9be - throw runtime_error("marmaracreditloop txid\n"); - } - if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); -#endif - - txid = Parseuint256((char *)params[0].get_str().c_str()); - result = MarmaraCreditloop(remotepk, txid); - return(result); -} - -UniValue marmara_settlement(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); uint256 batontxid; - if ( fHelp || params.size() != 1 ) - { - // marmarasettlement 010ff7f9256cefe3b5dee3d72c0eeae9fc6f34884e6f32ffe5b60916df54a9be - // marmarasettlement ff3e259869196f3da9b5ea3f9e088a76c4fc063cf36ab586b652e121d441a603 - throw runtime_error("marmarasettlement batontxid (discontinued)\n"); - } - if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - - throw runtime_error("marmarasettlement is discontinued\n"); - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); -#endif - - /* batontxid = Parseuint256((char *)params[0].get_str().c_str()); - CTransaction tx; - result = MarmaraSettlement(0,batontxid, tx); */ - return(result); -} - -UniValue marmara_lock(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); int64_t amount; int32_t height; - if ( fHelp || (params.size() < 1 || params.size() > 2)) - { - throw runtime_error("marmaralock amount [pubkey]\n" - "converts normal coins to activated coins\n" "\n"); - } - if (ensure_CCrequirements(EVAL_MARMARA) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); -#endif - - amount = atof(params[0].get_str().c_str()) * COIN + 0.00000000499999; - - CPubKey destPk; // created empty - if (params.size() == 2) { - vuint8_t vpubkey = ParseHex(params[1].get_str().c_str()); - destPk = pubkey2pk(vpubkey); - } - - result = MarmaraLock(remotepk, 0, amount, destPk); - return result; -} - -// generate new activated address and output its segid -UniValue marmara_newaddress(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - if (fHelp || params.size() != 0) - { - throw runtime_error("marmaranewaddress\n"); - } - if (ensure_CCrequirements(EVAL_MARMARA) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - std::string strAccount; - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - LOCK2(cs_main, pwalletMain->cs_wallet); - if (!pwalletMain->IsLocked()) - pwalletMain->TopUpKeyPool(); - - // Generate a new key that is added to wallet - CPubKey newPubKey; - if (!pwalletMain->GetKeyFromPool(newPubKey)) - throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); - CKeyID keyID = newPubKey.GetID(); - - pwalletMain->SetAddressBook(keyID, strAccount, "receive"); - - result = MarmaraNewActivatedAddress(newPubKey); -#else - result.push_back(std::make_pair("result", "error")); - result.push_back(std::make_pair("error", "wallet unavailable")); -#endif - return result; -} - -// marmaralock64 rpc impl, create 64 activated addresses for each segid and add amount / 64 -UniValue marmara_lock64(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - CCerror.clear(); - if (fHelp || params.size() != 2) - { - throw runtime_error("marmaralock64 amount num\n" - "generates 64 activated addresses in the wallet and distributes 'amount' in coins on the addresses creating 'num' utxos on each address\n" "\n"); - } - if (ensure_CCrequirements(EVAL_MARMARA) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - LOCK2(cs_main, pwalletMain->cs_wallet); - - EnsureWalletIsUnlocked(); - //if (!pwalletMain->IsLocked()) - // pwalletMain->TopUpKeyPool(); - - CAmount amount = (CAmount)(atof(params[0].get_str().c_str()) * (double)COIN); - if (amount <= 0) - throw runtime_error("amount should be > 0\n"); - - int32_t nutxos = atoi(params[1].get_str().c_str()); - if (nutxos <= 0) - throw runtime_error("num should be > 0\n"); - - std::string hextx = MarmaraLock64(pwalletMain, amount, nutxos); - RETURN_IF_ERROR(CCerror); - - result.push_back(std::make_pair("result", "success")); - result.push_back(std::make_pair("hextx", hextx)); -#else - result.push_back(std::make_pair("result", "error")); - result.push_back(std::make_pair("error", "wallet unavailable")); -#endif - return result; -} - -// marmaralistactivated rpc impl, lists activated addresses in the wallet and return amounts on these addresses -UniValue marmara_listactivatedaddresses(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - if (fHelp || params.size() != 0) - { - throw runtime_error("marmaralistactivatedaddresses\n" - "list activated addresses in the wallet and returns amount on the addresses\n" "\n"); - } - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - LOCK2(cs_main, pwalletMain->cs_wallet); - EnsureWalletIsUnlocked(); - - result = MarmaraListActivatedAddresses(pwalletMain); -#else - result.push_back(std::make_pair("result", "error")); - result.push_back(std::make_pair("error", "wallet unavailable")); -#endif - return result; -} - -// marmarareleaseactivatedcoins rpc impl, collects activated utxos in the wallet and sends the amount to the address param -UniValue marmara_releaseactivatedcoins(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - CCerror.clear(); - if (fHelp || params.size() != 1) - { - throw runtime_error("marmarareleaseactivatedcoins address\n" - "collects activated utxos in the wallet and sends the amount to the normal 'address'\n" "\n"); - } - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - EnsureWalletIsUnlocked(); - - std::string dest = params[0].get_str(); - std::string hextx = MarmaraReleaseActivatedCoins(pwalletMain, dest); - RETURN_IF_ERROR(CCerror); - - result.push_back(std::make_pair("result", "success")); - result.push_back(std::make_pair(JSON_HEXTX, hextx)); -#else - result.push_back(std::make_pair("result", "error")); - result.push_back(std::make_pair("error", "wallet unavailable")); -#endif - return result; -} - -// marmaraposstat rpc impl, return PoS statistics -UniValue marmara_posstat(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - CCerror.clear(); - if (fHelp || params.size() != 2) - { - throw runtime_error("marmaraposstat begin-height end-height\n" - "returns PoS statistics for the marmara chain from begin-height to end-height block.\n" - "If begin-height is 0 the statistics is collected from the beginning of the chain\n" - "If end-height is 0 the statistics is collected to the last block of the chain\n" "\n"); - } - - int32_t beginHeight = atoi(params[0].get_str().c_str()); - if (beginHeight < 0 || beginHeight > chainActive.Height()) - throw runtime_error("begin-height out of block range\n"); - int32_t endHeight = atoi(params[1].get_str().c_str()); - if (endHeight < 0 || endHeight > chainActive.Height()) - throw runtime_error("end-height out of block range\n"); - - UniValue result = MarmaraPoSStat(beginHeight, endHeight); - RETURN_IF_ERROR(CCerror); - return result; -} - -// marmaraposstat rpc impl, return PoS statistics -UniValue marmara_unlock(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - CCerror.clear(); - if (fHelp || params.size() != 1) - { - throw runtime_error("marmaraunlock satoshis\n" - "unlocks activated coins on my pubkey and sends coins to normal address.\n" "\n"); - } - -#ifdef ENABLE_WALLET - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - - EnsureWalletIsUnlocked(); - - CAmount sat = atoll(params[0].get_str().c_str()); - result = MarmaraUnlockActivatedCoins(sat); - RETURN_IF_ERROR(CCerror); -#else - result.push_back(std::make_pair("result", "error")); - result.push_back(std::make_pair("error", "wallet unavailable")); -#endif - return result; -} - - -static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode - // -------------- ------------------------ ----------------------- ---------- - // Marmara - { "marmara", "marmaraaddress", &marmaraaddress, true }, - { "marmara", "marmarapoolpayout", &marmara_poolpayout, true }, - { "marmara", "marmarareceive", &marmara_receive, true }, - { "marmara", "marmaraissue", &marmara_issue, true }, - { "marmara", "marmaratransfer", &marmara_transfer, true }, - { "marmara", "marmarainfo", &marmara_info, true }, - { "marmara", "marmaracreditloop", &marmara_creditloop, true }, - { "marmara", "marmarasettlement", &marmara_settlement, true }, - { "marmara", "marmaralock", &marmara_lock, true }, - { "marmara", "marmaranewaddress", &marmara_newaddress, true }, - { "marmara", "marmaralock64", &marmara_lock64, true }, - { "marmara", "marmaralistactivatedaddresses", &marmara_listactivatedaddresses, true }, - { "marmara", "marmarareleaseactivatedcoins", &marmara_releaseactivatedcoins, true }, - { "marmara", "marmaraposstat", &marmara_posstat, true }, - { "marmara", "marmaraunlock", &marmara_unlock, true }, -}; - -void RegisterMarmaraRPCCommands(CRPCTable &tableRPC) -{ - for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) - tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); -} diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index d043e7796ec..b05517ae0a9 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -25,7 +25,6 @@ #include "net.h" #include "netbase.h" #include "rpc/server.h" -#include "timedata.h" #include "txmempool.h" #include "util.h" #include "notaries_staked.h" @@ -65,7 +64,7 @@ int32_t Jumblr_secretaddradd(char *secretaddr); uint64_t komodo_interestsum(); int32_t komodo_longestchain(); int32_t komodo_notarized_height(int32_t *prevMoMheightp,uint256 *hashp,uint256 *txidp); -extern bool komodo_txnotarizedconfirmed(uint256 txid,int32_t minconfirms); +bool komodo_txnotarizedconfirmed(uint256 txid); uint32_t komodo_chainactive_timestamp(); int32_t komodo_whoami(char *pubkeystr,int32_t height,uint32_t timestamp); extern uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE; @@ -78,7 +77,7 @@ int8_t StakedNotaryID(std::string ¬aryname, char *Raddress); uint64_t komodo_notarypayamount(int32_t nHeight, int64_t notarycount); int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); -#define KOMODO_VERSION "0.5.3" +#define KOMODO_VERSION "0.6.1" #define VERUS_VERSION "0.4.0g" extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; extern uint32_t ASSETCHAINS_CC; @@ -206,7 +205,7 @@ UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) " \"walletversion\": xxxxx, (numeric) the wallet version\n" " \"balance\": xxxxxxx, (numeric) the total Komodo balance of the wallet\n" " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" + " \"timeoffset\": xxxxx, (numeric) the time offset (deprecated; always 0)\n" " \"connections\": xxxxx, (numeric) the number of connections\n" " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" @@ -287,7 +286,7 @@ UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) #endif obj.push_back(Pair("sapling", ASSETCHAINS_SAPLING)); } - obj.push_back(Pair("timeoffset", GetTimeOffset())); + obj.push_back(Pair("timeoffset", 0)); obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()))); obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); @@ -1532,7 +1531,7 @@ UniValue getspentinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) "\nArguments:\n" "{\n" " \"txid\" (string) The hex string of the txid\n" - " \"index\" (number) The output index in tx\n" + " \"index\" (number) The start block height\n" "}\n" "\nResult:\n" "{\n" @@ -1577,7 +1576,7 @@ UniValue txnotarizedconfirmed(const UniValue& params, bool fHelp, const CPubKey& if (fHelp || params.size() < 1 || params.size() > 1) { string msg = "txnotarizedconfirmed txid\n" - "\nReturns true if transaction is notarized on chain that has dPoW or if confirmation number is greater than 60 on chain taht does not have dPoW.\n" + "\nReturns true if transaction is notarized on chain that has dPoW or if confirmation number is greater than 60 on chain that does not have dPoW.\n" "\nArguments:\n" "1. txid (string, required) Transaction id.\n" @@ -1599,7 +1598,7 @@ UniValue txnotarizedconfirmed(const UniValue& params, bool fHelp, const CPubKey& UniValue decodeccopret(const UniValue& params, bool fHelp, const CPubKey& mypk) { CTransaction tx; uint256 tokenid,txid,hashblock; - std::vector vopret,vOpretExtra; uint8_t *script; + std::vector vopret,vOpretExtra; uint8_t *script,tokenevalcode; UniValue result(UniValue::VOBJ),array(UniValue::VARR); std::vector pubkeys; if (fHelp || params.size() < 1 || params.size() > 1) @@ -1620,12 +1619,11 @@ UniValue decodeccopret(const UniValue& params, bool fHelp, const CPubKey& mypk) } std::vector hex(ParseHex(params[0].get_str())); CScript scripthex(hex.begin(),hex.end()); - std::vector oprets; - if (DecodeTokenOpRetV1(scripthex,tokenid,pubkeys, oprets)!=0 && oprets.size()>0) + std::vector> oprets; + if (DecodeTokenOpRet(scripthex,tokenevalcode,tokenid,pubkeys, oprets)!=0 && tokenevalcode==EVAL_TOKENS && oprets.size()>0) { // seems we need a loop here - if (oprets.size() > 0) - vOpretExtra = oprets[0]; + vOpretExtra = oprets[0].second; UniValue obj(UniValue::VOBJ); GetOpReturnData(scripthex,vopret); script = (uint8_t *)vopret.data(); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 87c3eac6ce3..f87d953eaf1 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -25,7 +25,6 @@ #include "netbase.h" #include "protocol.h" #include "sync.h" -#include "timedata.h" #include "util.h" #include "version.h" #include "deprecation.h" @@ -77,19 +76,6 @@ UniValue ping(const UniValue& params, bool fHelp, const CPubKey& mypk) return NullUniValue; } -static void CopyNodeStats(std::vector& vstats) -{ - vstats.clear(); - - LOCK(cs_vNodes); - vstats.reserve(vNodes.size()); - BOOST_FOREACH(CNode* pnode, vNodes) { - CNodeStats stats; - pnode->copyStats(stats); - vstats.push_back(stats); - } -} - UniValue getpeerinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) { if (fHelp || params.size() != 0) @@ -108,7 +94,7 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) " \"bytessent\": n, (numeric) The total bytes sent\n" " \"bytesrecv\": n, (numeric) The total bytes received\n" " \"conntime\": ttt, (numeric) The connection time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"timeoffset\": ttt, (numeric) The time offset in seconds\n" + " \"timeoffset\": xxxxx, (numeric) the time offset (deprecated; always 0)\n" " \"pingtime\": n, (numeric) ping time\n" " \"pingwait\": n, (numeric) ping wait\n" " \"version\": v, (numeric) The peer version, such as 170002\n" @@ -145,13 +131,18 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) obj.push_back(Pair("addr", stats.addrName)); if (!(stats.addrLocal.empty())) obj.push_back(Pair("addrlocal", stats.addrLocal)); + // if (stats.addrBind.IsValid()) + // obj.push_back(Pair("addrbind", stats.addrBind.ToString())); + if (stats.m_mapped_as != 0) { + obj.push_back(Pair("mapped_as", uint64_t(stats.m_mapped_as))); + } obj.push_back(Pair("services", strprintf("%016x", stats.nServices))); obj.push_back(Pair("lastsend", stats.nLastSend)); obj.push_back(Pair("lastrecv", stats.nLastRecv)); obj.push_back(Pair("bytessent", stats.nSendBytes)); obj.push_back(Pair("bytesrecv", stats.nRecvBytes)); obj.push_back(Pair("conntime", stats.nTimeConnected)); - obj.push_back(Pair("timeoffset", stats.nTimeOffset)); + obj.push_back(Pair("timeoffset", 0)); obj.push_back(Pair("pingtime", stats.dPingTime)); if (stats.dPingWait > 0.0) obj.push_back(Pair("pingwait", stats.dPingWait)); @@ -550,7 +541,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) obj.push_back(Pair("subversion", strSubVersion)); obj.push_back(Pair("protocolversion",PROTOCOL_VERSION)); obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices))); - obj.push_back(Pair("timeoffset", GetTimeOffset())); + obj.push_back(Pair("timeoffset", 0)); obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("networks", GetNetworksInfo())); obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); diff --git a/src/rpc/pricesrpc.cpp b/src/rpc/pricesrpc.cpp deleted file mode 100644 index 435048e7b58..00000000000 --- a/src/rpc/pricesrpc.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/****************************************************************************** -* Copyright © 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ -#include -#include -#include - -#include "server.h" -#include "cc/CCPrices.h" -#include "cc/pricesfeed.h" - -using namespace std; - - -// fills pricedata with raw price, correlated and smoothed values for numblock -/* int32_t prices_extract(int64_t *pricedata, int32_t firstheight, int32_t numblocks, int32_t ind) -{ - int32_t height, i, n, width, numpricefeeds = -1; uint64_t seed, ignore, rngval; uint32_t rawprices[1440 * 6], *ptr; int64_t *tmpbuf; - width = numblocks + PRICES_DAYWINDOW * 2 + PRICES_SMOOTHWIDTH; // need 2*PRICES_DAYWINDOW previous raw price points to calc PRICES_DAYWINDOW correlated points to calc, in turn, smoothed point - komodo_heightpricebits(&seed, rawprices, firstheight + numblocks - 1); - if (firstheight < width) - return(-1); - for (i = 0; i < width; i++) - { - if ((n = komodo_heightpricebits(&ignore, rawprices, firstheight + numblocks - 1 - i)) < 0) // stores raw prices in backward order - return(-1); - if (numpricefeeds < 0) - numpricefeeds = n; - if (n != numpricefeeds) - return(-2); - ptr = (uint32_t *)&pricedata[i * 3]; - ptr[0] = rawprices[ind]; - ptr[1] = rawprices[0]; // timestamp - } - rngval = seed; - for (i = 0; i < numblocks + PRICES_DAYWINDOW + PRICES_SMOOTHWIDTH; i++) // calculates +PRICES_DAYWINDOW more correlated values - { - rngval = (rngval * 11109 + 13849); - ptr = (uint32_t *)&pricedata[i * 3]; - // takes previous PRICES_DAYWINDOW raw prices and calculates correlated price value - if ((pricedata[i * 3 + 1] = komodo_pricecorrelated(rngval, ind, (uint32_t *)&pricedata[i * 3], 6, 0, PRICES_SMOOTHWIDTH)) < 0) // skip is 6 == sizeof(int64_t)/sizeof(int32_t)*3 - return(-3); - } - tmpbuf = (int64_t *)calloc(sizeof(int64_t), 2 * PRICES_DAYWINDOW); - for (i = 0; i < numblocks; i++) - // takes previous PRICES_DAYWINDOW correlated price values and calculates smoothed value - pricedata[i * 3 + 2] = komodo_priceave(tmpbuf, &pricedata[i * 3 + 1], 3); - free(tmpbuf); - return(0); -} */ - -UniValue prices(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 1) - throw runtime_error("prices maxsamples\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - uint64_t seed, rngval; - int64_t *tmpbuf, smoothed, *correlated, checkprices[PRICES_MAXDATAPOINTS]; - char name[64], *str; - uint32_t rawprices[1440 * 6], *prices = NULL; - int32_t ival, width, numpricefeeds = -1, n, numsamples, nextheight, ht; - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - int32_t maxsamples = atoi(params[0].get_str().c_str()); - if (maxsamples < 1) - maxsamples = 1; - nextheight = komodo_nextheight(); - UniValue a(UniValue::VARR); - if (PRICES_DAYWINDOW < 7) - throw JSONRPCError(RPC_INVALID_PARAMETER, "daywindow is too small"); - width = maxsamples + 2 * PRICES_DAYWINDOW + PRICES_SMOOTHWIDTH; - numpricefeeds = komodo_heightpricebits(&seed, rawprices, nextheight - 1); - if (numpricefeeds <= 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "illegal numpricefeeds"); - prices = (uint32_t *)calloc(sizeof(*prices), width*numpricefeeds); - correlated = (int64_t *)calloc(sizeof(*correlated), width); - ival = 0; - for (ht = nextheight - 1, ival = 0; ival2; ival++, ht--) - { - if (ht < 0 || ht > chainActive.Height()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - else - { - if ((n = komodo_heightpricebits(0, rawprices, ht)) > 0) - { - if (n != numpricefeeds) - throw JSONRPCError(RPC_INVALID_PARAMETER, "numprices at some height != tip numprices"); - else - { - for (int32_t index = 0; index < numpricefeeds; index++) - prices[index*width + ival] = rawprices[index]; - } - } - else - throw JSONRPCError(RPC_INVALID_PARAMETER, "no komodo_rawprices found"); - } - } - numsamples = ival; - ret.push_back(Pair("firstheight", (int64_t)nextheight - 1 - ival)); - UniValue timestamps(UniValue::VARR); - for (int32_t i = 0; i processors; - PricesFeedGetCustomProcessors(processors); - - for (int32_t index = 1; index < numpricefeeds; index++) - { - CustomConverter priceConverter = NULL; - for (auto const &p : processors) - if (index >= p.b && index < p.b) - priceConverter = p.converter; - - UniValue item(UniValue::VOBJ), p(UniValue::VARR); - if ((str = komodo_pricename(name, index)) != 0) - { - item.push_back(Pair("name", str)); - if (priceConverter != NULL) - { - for (int32_t i = 0; i < maxsamples + PRICES_DAYWINDOW + PRICES_SMOOTHWIDTH && i < numsamples; i++) - { - UniValue parr(UniValue::VARR); - int64_t converted; - int32_t offset = index * width + i; - priceConverter(index, prices[offset], &converted); - parr.push_back(ValueFromAmount(converted)); - } - } - else - { - if (numsamples >= width) - { - for (int32_t i = 0; i < maxsamples + PRICES_DAYWINDOW + PRICES_SMOOTHWIDTH && i < numsamples; i++) - { - int32_t offset = index * width + i; - rngval = (rngval * 11109 + 13849); - if ((correlated[i] = komodo_pricecorrelated(rngval, index, &prices[offset], 1, 0, PRICES_SMOOTHWIDTH)) < 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "null correlated price"); - { - if (komodo_priceget(checkprices, index, nextheight - 1 - i, 1) >= 0) - { - if (checkprices[1] != correlated[i]) - { - //fprintf(stderr,"ind.%d ht.%d %.8f != %.8f\n",j,nextheight-1-i,(double)checkprices[1]/COIN,(double)correlated[i]/COIN); - correlated[i] = checkprices[1]; - } - } - } - } - tmpbuf = (int64_t *)calloc(sizeof(int64_t), 2 * PRICES_DAYWINDOW); - for (int32_t i = 0; i < maxsamples && i < numsamples; i++) - { - int32_t offset = index * width + i; - smoothed = komodo_priceave(tmpbuf, &correlated[i], 1); - if (komodo_priceget(checkprices, index, nextheight - 1 - i, 1) >= 0) - { - if (checkprices[2] != smoothed) - { - fprintf(stderr, "ind.%d ht.%d %.8f != %.8f\n", index, nextheight - 1 - i, (double)checkprices[2] / COIN, (double)smoothed / COIN); - smoothed = checkprices[2]; - } - } - UniValue parr(UniValue::VARR); - parr.push_back(ValueFromAmount((int64_t)prices[offset] * komodo_pricemult_to10e8(index))); - parr.push_back(ValueFromAmount(correlated[i])); // correlated values returned normalized to 100,000,000 order for creating synthetic indexes - parr.push_back(ValueFromAmount(smoothed)); // smoothed values returned normalized to 100,000,000 order for creating synthetic indexes - // compare to alternate method - p.push_back(parr); - } - free(tmpbuf); - } - else - { - for (int32_t i = 0; i < maxsamples && i < numsamples; i++) - { - int32_t offset = index * width + i; - UniValue parr(UniValue::VARR); - parr.push_back(ValueFromAmount((int64_t)prices[offset] * komodo_pricemult_to10e8(index))); - p.push_back(parr); - } - } - } - item.push_back(Pair("prices", p)); - } - else - item.push_back(Pair("name", "error")); - a.push_back(item); - } - ret.push_back(Pair("pricefeeds", a)); - ret.push_back(Pair("result", "success")); - ret.push_back(Pair("seed", (int64_t)seed)); - ret.push_back(Pair("height", (int64_t)nextheight - 1)); - ret.push_back(Pair("maxsamples", (int64_t)maxsamples)); - ret.push_back(Pair("width", (int64_t)width)); - ret.push_back(Pair("daywindow", (int64_t)PRICES_DAYWINDOW)); - ret.push_back(Pair("numpricefeeds", (int64_t)numpricefeeds)); - free(prices); - free(correlated); - return ret; -} - -// pricesbet rpc implementation -UniValue pricesbet(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 3) - throw runtime_error("pricesbet amount leverage \"synthetic-expression\"\n" - "amount is in coins\n" - "leverage is integer non-zero value, positive for long, negative for short position\n" - "synthetic-expression examples:\n" - " \"BTC_USD, 1\" - BTC to USD index with mult 1\n" - " 'BTC_USD,!,1' - USD to BTC index with mult 1 (use apostrophes to escape '!' in linux bash)\n" - " \"BTC_USD, KMD_BTC, INR_USD, **/ , 1\" is actually KMD to INR index with mult 1\n" "\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - CAmount txfee = 10000; - CAmount amount = atof(params[0].get_str().c_str()) * COIN; - int16_t leverage = (int16_t)atoi(params[1].get_str().c_str()); - if (leverage == 0) - throw runtime_error("invalid leverage\n"); - - std::string sexpr = params[2].get_str(); - std::vector vexpr; - SplitStr(sexpr, vexpr); - - // debug print parsed strings: - std::cerr << "parsed synthetic: "; - for (auto s : vexpr) - std::cerr << s << " "; - std::cerr << std::endl; - - return PricesBet(txfee, amount, leverage, vexpr); -} - -// pricesaddfunding rpc implementation -UniValue pricesaddfunding(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 2) - throw runtime_error("pricesaddfunding bettxid amount\n" - "where amount is in coins\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - CAmount txfee = 10000; - uint256 bettxid = Parseuint256(params[0].get_str().c_str()); - if (bettxid.IsNull()) - throw runtime_error("invalid bettxid\n"); - - CAmount amount = atof(params[1].get_str().c_str()) * COIN; - if (amount <= 0) - throw runtime_error("invalid amount\n"); - - return PricesAddFunding(txfee, bettxid, amount); -} - -// rpc pricessetcostbasis implementation -UniValue pricessetcostbasis(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 1) - throw runtime_error("pricessetcostbasis bettxid\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - uint256 bettxid = Parseuint256(params[0].get_str().c_str()); - if (bettxid.IsNull()) - throw runtime_error("invalid bettxid\n"); - - int64_t txfee = 10000; - - return PricesSetcostbasis(txfee, bettxid); -} - -// pricescashout rpc implementation -UniValue pricescashout(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 1) - throw runtime_error("pricescashout bettxid\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - uint256 bettxid = Parseuint256(params[0].get_str().c_str()); - if (bettxid.IsNull()) - throw runtime_error("invalid bettxid\n"); - - int64_t txfee = 10000; - - return PricesCashout(txfee, bettxid); -} - -// pricesrekt rpc implementation -UniValue pricesrekt(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 2) - throw runtime_error("pricesrekt bettxid height\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - uint256 bettxid = Parseuint256(params[0].get_str().c_str()); - if (bettxid.IsNull()) - throw runtime_error("invalid bettxid\n"); - - int32_t height = atoi(params[0].get_str().c_str()); - - int64_t txfee = 10000; - - return PricesRekt(txfee, bettxid, height); -} - -// pricesrekt rpc implementation -UniValue pricesgetorderbook(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 0) - throw runtime_error("pricesgetorderbook\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - return PricesGetOrderbook(); -} - -// pricesrekt rpc implementation -UniValue pricesrefillfund(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 1) - throw runtime_error("pricesrefillfund amount\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - CAmount amount = atof(params[0].get_str().c_str()) * COIN; - - return PricesRefillFund(amount); -} - diff --git a/src/rpc/register.h b/src/rpc/register.h index 0f0afb199b7..245f76e2220 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -34,10 +34,10 @@ void RegisterMiscRPCCommands(CRPCTable &tableRPC); void RegisterMiningRPCCommands(CRPCTable &tableRPC); /** Register raw transaction RPC commands */ void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC); -// tokens cc rpcs: -void RegisterTokensRPCCommands(CRPCTable &tableRPC); -// marmara cc rpcs: -void RegisterMarmaraRPCCommands(CRPCTable &tableRPC); + +/** Register test transaction RPC commands */ +void RegisterTesttransactionsRPCCommands(CRPCTable &tableRPC); + static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) { @@ -46,8 +46,9 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) RegisterMiscRPCCommands(tableRPC); RegisterMiningRPCCommands(tableRPC); RegisterRawTransactionRPCCommands(tableRPC); - RegisterTokensRPCCommands(tableRPC); - RegisterMarmaraRPCCommands(tableRPC); +#ifdef TESTMODE + RegisterTesttransactionsRPCCommands(tableRPC); +#endif } #endif diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index a559b2bb7c6..1416d3bc72d 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -362,17 +362,16 @@ static const CRPCCommand vRPCCommands[] = { "crosschain", "selfimport", &selfimport, true }, { "crosschain", "importdual", &importdual, true }, //ImportGateway - { "crosschain", "importgatewayaddress", &importgatewayaddress, true }, + { "crosschain", "importgatewayddress", &importgatewayaddress, true }, { "crosschain", "importgatewayinfo", &importgatewayinfo, true }, { "crosschain", "importgatewaybind", &importgatewaybind, true }, { "crosschain", "importgatewaydeposit", &importgatewaydeposit, true }, { "crosschain", "importgatewaywithdraw", &importgatewaywithdraw, true }, - { "crosschain", "importgatewaywithdrawsign", &importgatewaywithdrawsign, true }, + { "crosschain", "importgatewaypartialsign", &importgatewaypartialsign, true }, + { "crosschain", "importgatewaycompletesigning", &importgatewaycompletesigning, true }, { "crosschain", "importgatewaymarkdone", &importgatewaymarkdone, true }, - { "crosschain", "importgatewaypendingsignwithdraws", &importgatewaypendingsignwithdraws, true }, - { "crosschain", "importgatewaysignedwithdraws", &importgatewaysignedwithdraws, true }, - { "crosschain", "importgatewayexternaladdress", &importgatewayexternaladdress, true }, - { "crosschain", "importgatewaydumpprivkey", &importgatewaydumpprivkey, true }, + { "crosschain", "importgatewaypendingwithdraws", &importgatewaypendingwithdraws, true }, + { "crosschain", "importgatewayprocessed", &importgatewayprocessed, true }, @@ -417,21 +416,6 @@ static const CRPCCommand vRPCCommands[] = { "FSM", "FSMlist", &FSMlist, true }, { "FSM", "FSMinfo", &FSMinfo, true }, - // DEX - { "DEX", "DEX_broadcast", &DEX_broadcast, true }, - { "DEX", "DEX_anonsend", &DEX_anonsend, true }, - { "DEX", "DEX_list", &DEX_list, true }, - { "DEX", "DEX_get", &DEX_get, true }, - { "DEX", "DEX_stats", &DEX_stats, true }, - { "DEX", "DEX_orderbook", &DEX_orderbook, true }, - { "DEX", "DEX_cancel", &DEX_cancel, true }, - { "DEX", "DEX_setpubkey", &DEX_setpubkey, true }, - { "DEX", "DEX_publish", &DEX_publish, true }, - { "DEX", "DEX_subscribe", &DEX_subscribe, true }, - { "DEX", "DEX_stream", &DEX_stream, true }, - { "DEX", "DEX_streamsub", &DEX_streamsub, true }, - { "DEX", "DEX_notarize", &DEX_notarize, true }, - // fsm { "nSPV", "nspv_getinfo", &nspv_getinfo, true }, { "nSPV", "nspv_login", &nspv_login, true }, @@ -479,7 +463,6 @@ static const CRPCCommand vRPCCommands[] = { "channels", "channelsinfo", &channelsinfo, true }, { "channels", "channelsopen", &channelsopen, true }, { "channels", "channelspayment", &channelspayment, true }, - { "channels", "channelsgeneratesecret", &channelsgeneratesecret, true }, { "channels", "channelsclose", &channelsclose, true }, { "channels", "channelsrefund", &channelsrefund, true }, @@ -512,6 +495,17 @@ static const CRPCCommand vRPCCommands[] = // Pegs { "pegs", "pegsaddress", &pegsaddress, true }, + // Marmara + { "marmara", "marmaraaddress", &marmaraaddress, true }, + { "marmara", "marmarapoolpayout", &marmara_poolpayout, true }, + { "marmara", "marmarareceive", &marmara_receive, true }, + { "marmara", "marmaraissue", &marmara_issue, true }, + { "marmara", "marmaratransfer", &marmara_transfer, true }, + { "marmara", "marmarainfo", &marmara_info, true }, + { "marmara", "marmaracreditloop", &marmara_creditloop, true }, + { "marmara", "marmarasettlement", &marmara_settlement, true }, + { "marmara", "marmaralock", &marmara_lock, true }, + // Payments { "payments", "paymentsaddress", &paymentsaddress, true }, { "payments", "paymentstxidopret", &payments_txidopret, true }, @@ -536,13 +530,14 @@ static const CRPCCommand vRPCCommands[] = { "gateways", "gatewaysinfo", &gatewaysinfo, true }, { "gateways", "gatewaysbind", &gatewaysbind, true }, { "gateways", "gatewaysdeposit", &gatewaysdeposit, true }, + { "gateways", "gatewaysclaim", &gatewaysclaim, true }, { "gateways", "gatewayswithdraw", &gatewayswithdraw, true }, - { "gateways", "gatewayswithdrawsign", &gatewayswithdrawsign, true }, + { "gateways", "gatewayspartialsign", &gatewayspartialsign, true }, + { "gateways", "gatewayscompletesigning", &gatewayscompletesigning, true }, { "gateways", "gatewaysmarkdone", &gatewaysmarkdone, true }, { "gateways", "gatewayspendingdeposits", &gatewayspendingdeposits, true }, - { "gateways", "gatewayspendingsignwithdraws", &gatewayspendingsignwithdraws, true }, - { "gateways", "gatewayssignedwithdraws", &gatewayssignedwithdraws, true }, - + { "gateways", "gatewayspendingwithdraws", &gatewayspendingwithdraws, true }, + { "gateways", "gatewaysprocessed", &gatewaysprocessed, true }, // dice { "dice", "dicelist", &dicelist, true }, @@ -554,12 +549,31 @@ static const CRPCCommand vRPCCommands[] = { "dice", "dicestatus", &dicestatus, true }, { "dice", "diceaddress", &diceaddress, true }, + // tokens & assets + { "tokens", "assetsaddress", &assetsaddress, true }, + { "tokens", "tokeninfo", &tokeninfo, true }, + { "tokens", "tokenlist", &tokenlist, true }, + { "tokens", "tokenorders", &tokenorders, true }, + { "tokens", "mytokenorders", &mytokenorders, true }, + { "tokens", "tokenaddress", &tokenaddress, true }, + { "tokens", "tokenbalance", &tokenbalance, true }, + { "tokens", "tokencreate", &tokencreate, true }, + { "tokens", "tokentransfer", &tokentransfer, true }, + { "tokens", "tokenbid", &tokenbid, true }, + { "tokens", "tokencancelbid", &tokencancelbid, true }, + { "tokens", "tokenfillbid", &tokenfillbid, true }, + { "tokens", "tokenask", &tokenask, true }, + //{ "tokens", "tokenswapask", &tokenswapask, true }, + { "tokens", "tokencancelask", &tokencancelask, true }, + { "tokens", "tokenfillask", &tokenfillask, true }, + //{ "tokens", "tokenfillswap", &tokenfillswap, true }, + { "tokens", "tokenconvert", &tokenconvert, true }, + // pegs { "pegs", "pegscreate", &pegscreate, true }, { "pegs", "pegsfund", &pegsfund, true }, { "pegs", "pegsget", &pegsget, true }, { "pegs", "pegsredeem", &pegsredeem, true }, - { "pegs", "pegsclose", &pegsclose, true }, { "pegs", "pegsliquidate", &pegsliquidate, true }, { "pegs", "pegsexchange", &pegsexchange, true }, { "pegs", "pegsaccounthistory", &pegsaccounthistory, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 2b40b631e84..465bc832b56 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -241,6 +241,22 @@ extern UniValue submitblock(const UniValue& params, bool fHelp, const CPubKey& m extern UniValue estimatefee(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue estimatepriority(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue coinsupply(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokeninfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokenlist(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokenorders(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue mytokenorders(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokenbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue assetsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokenaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokencreate(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokentransfer(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokenbid(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokencancelbid(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokenfillbid(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokenask(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokencancelask(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokenfillask(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue tokenconvert(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue heiraddress(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue heirfund(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue heiradd(const UniValue& params, bool fHelp, const CPubKey& mypk); @@ -263,6 +279,15 @@ extern UniValue priceslist(const UniValue& params, bool fHelp, const CPubKey& my extern UniValue mypriceslist(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue pricesinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue pegsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue marmaraaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue marmara_poolpayout(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue marmara_receive(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue marmara_issue(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue marmara_transfer(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue marmara_info(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue marmara_creditloop(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue marmara_settlement(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue marmara_lock(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue paymentsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue payments_release(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue payments_fund(const UniValue& params, bool fHelp, const CPubKey& mypk); @@ -273,6 +298,7 @@ extern UniValue payments_airdrop(const UniValue& params, bool fHelp, const CPubK extern UniValue payments_airdroptokens(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue payments_info(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue payments_list(const UniValue& params, bool fHelp, const CPubKey& mypk); + extern UniValue cclibaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue cclibinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue cclib(const UniValue& params, bool fHelp, const CPubKey& mypk); @@ -283,17 +309,18 @@ extern UniValue gatewaysdumpprivkey(const UniValue& params, bool fHelp, const CP extern UniValue gatewaysexternaladdress(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue gatewaysbind(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue gatewaysdeposit(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue gatewaysclaim(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue gatewayswithdraw(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue gatewayswithdrawsign(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue gatewayspartialsign(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue gatewayscompletesigning(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue gatewaysmarkdone(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue gatewayspendingdeposits(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue gatewayspendingsignwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue gatewayssignedwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue gatewayspendingwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue gatewaysprocessed(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue channelslist(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue channelsinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue channelsopen(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue channelspayment(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue channelsgeneratesecret(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue channelsclose(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue channelsrefund(const UniValue& params, bool fHelp, const CPubKey& mypk); //extern UniValue tokenswapask(const UniValue& params, bool fHelp, const CPubKey& mypk); @@ -327,7 +354,6 @@ extern UniValue pegscreate(const UniValue& params, bool fHelp, const CPubKey& my extern UniValue pegsfund(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue pegsget(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue pegsredeem(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pegsclose(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue pegsliquidate(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue pegsexchange(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue pegsaccounthistory(const UniValue& params, bool fHelp, const CPubKey& mypk); @@ -434,13 +460,11 @@ extern UniValue importgatewayinfo(const UniValue& params, bool fHelp, const CPub extern UniValue importgatewaybind(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue importgatewaydeposit(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue importgatewaywithdraw(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue importgatewaywithdrawsign(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue importgatewaypartialsign(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue importgatewaycompletesigning(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue importgatewaymarkdone(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue importgatewaypendingsignwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue importgatewaysignedwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue importgatewayexternaladdress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue importgatewaydumpprivkey(const UniValue& params, bool fHelp, const CPubKey& mypk); - +extern UniValue importgatewaypendingwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue importgatewayprocessed(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue genminingCSV(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue nspv_getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); @@ -457,20 +481,6 @@ extern UniValue nspv_broadcast(const UniValue& params, bool fHelp, const CPubKey extern UniValue nspv_logout(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue nspv_listccmoduleunspent(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_broadcast(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_anonsend(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_list(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_get(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_stats(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_orderbook(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_cancel(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_setpubkey(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_publish(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_subscribe(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_stream(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_streamsub(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue DEX_notarize(const UniValue& params, bool fHelp, const CPubKey& mypk); - extern UniValue getblocksubsidy(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue z_exportkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp diff --git a/src/rpc/testtransactions.cpp b/src/rpc/testtransactions.cpp index 01fb368137f..c41e97e1036 100644 --- a/src/rpc/testtransactions.cpp +++ b/src/rpc/testtransactions.cpp @@ -162,7 +162,7 @@ UniValue test_burntx(const UniValue& params, bool fHelp, const CPubKey& mypk) std::vector vopret; GetNonfungibleData(tokenid, vopret); if (vopret.size() > 0) - cp->evalcodeNFT = vopret.begin()[0]; + cp->additionalTokensEvalcode2 = vopret.begin()[0]; uint8_t tokenpriv[33]; char unspendableTokenAddr[64]; diff --git a/src/rpc/tokensrpc.cpp b/src/rpc/tokensrpc.cpp deleted file mode 100644 index 86bc057982e..00000000000 --- a/src/rpc/tokensrpc.cpp +++ /dev/null @@ -1,732 +0,0 @@ -/****************************************************************************** - * Copyright 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include -#include -#include -#include "univalue.h" -#include "amount.h" -#include "rpc/server.h" -#include "rpc/protocol.h" - -#include "../wallet/crypter.h" -#include "../wallet/rpcwallet.h" - -#include "sync_ext.h" - -#include "../cc/CCinclude.h" -#include "../cc/CCtokens.h" -#include "../cc/CCassets.h" - - -using namespace std; - -UniValue assetsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - struct CCcontract_info *cp, C; std::vector pubkey; - cp = CCinit(&C, EVAL_ASSETS); - if (fHelp || params.size() > 1) - throw runtime_error("assetsaddress [pubkey]\n"); - if (ensure_CCrequirements(cp->evalcode) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - if (params.size() == 1) - pubkey = ParseHex(params[0].get_str().c_str()); - return(CCaddress(cp, (char *)"Assets", pubkey)); -} - -UniValue tokenaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - struct CCcontract_info *cp,C; std::vector pubkey; - cp = CCinit(&C, EVAL_TOKENS); - if ( fHelp || params.size() > 1 ) - throw runtime_error("tokenaddress [pubkey]\n"); - if ( ensure_CCrequirements(cp->evalcode) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - if ( params.size() == 1 ) - pubkey = ParseHex(params[0].get_str().c_str()); - return(CCaddress(cp,(char *)"Tokens", pubkey)); -} - -UniValue tokenlist(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - uint256 tokenid; - if ( fHelp || params.size() > 0 ) - throw runtime_error("tokenlist\n"); - if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - return(TokenList()); -} - -UniValue tokeninfo(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - uint256 tokenid; - if ( fHelp || params.size() != 1 ) - throw runtime_error("tokeninfo tokenid\n"); - if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - tokenid = Parseuint256((char *)params[0].get_str().c_str()); - return(TokenInfo(tokenid)); -} - -UniValue tokenorders(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - uint256 tokenid; - uint8_t evalcodeNFT = 0; - const CPubKey emptypk; - - if ( fHelp || params.size() > 2 ) - throw runtime_error("tokenorders [tokenid|'*'] [evalcode]\n" - "returns token orders for the tokenid or all available token orders if tokenid is not set\n" - "returns also NFT ask orders if NFT evalcode is set\n" "\n"); - if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - if (params.size() >= 1) - { - if (params[0].get_str() != "*") - { - tokenid = Parseuint256((char *)params[0].get_str().c_str()); - if (tokenid == zeroid) - throw runtime_error("incorrect tokenid\n"); - } - } - if (params.size() == 2) - evalcodeNFT = strtol(params[1].get_str().c_str(), NULL, 0); // supports also 0xEE-like values - - if (TokensIsVer1Active(NULL)) - return AssetOrders(tokenid, emptypk, evalcodeNFT); - else - return tokensv0::AssetOrders(tokenid, emptypk, evalcodeNFT); -} - - -UniValue mytokenorders(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - uint256 tokenid; - if (fHelp || params.size() > 1) - throw runtime_error("mytokenorders [evalcode]\n" - "returns all the token orders for mypubkey\n" - "if evalcode is set then returns mypubkey's token orders for non-fungible tokens with this evalcode\n" "\n"); - if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - uint8_t evalcodeNFT = 0; - if (params.size() == 1) - evalcodeNFT = strtol(params[0].get_str().c_str(), NULL, 0); // supports also 0xEE-like values - - CPubKey mypk; - SET_MYPK_OR_REMOTE(mypk, remotepk); - - if (TokensIsVer1Active(NULL)) - return AssetOrders(zeroid, mypk, evalcodeNFT); - else - return tokensv0::AssetOrders(zeroid, Mypubkey(), evalcodeNFT); - -} - -UniValue tokenbalance(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); uint256 tokenid; uint64_t balance; std::vector vpubkey; struct CCcontract_info *cp,C; - CCerror.clear(); - - if ( fHelp || params.size() < 1 || params.size() > 2 ) - throw runtime_error("tokenbalance tokenid [pubkey]\n"); - if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - - //LOCK(cs_main); - - tokenid = Parseuint256((char *)params[0].get_str().c_str()); - if ( params.size() == 2 ) - vpubkey = ParseHex(params[1].get_str().c_str()); - else - vpubkey = Mypubkey(); - - balance = GetTokenBalance(pubkey2pk(vpubkey),tokenid); - - if (CCerror.empty()) { - char destaddr[KOMODO_ADDRESS_BUFSIZE]; - - result.push_back(Pair("result", "success")); - cp = CCinit(&C, EVAL_TOKENS); - if (GetCCaddress(cp, destaddr, pubkey2pk(vpubkey)) != 0) - result.push_back(Pair("CCaddress", destaddr)); - - result.push_back(Pair("tokenid", params[0].get_str())); - result.push_back(Pair("balance", (int64_t)balance)); - } - else { - result = MakeResultError(CCerror); - } - - return(result); -} - -UniValue tokencreate(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - std::string name, description, hextx; - std::vector nonfungibleData; - int64_t supply; // changed from uin64_t to int64_t for this 'if ( supply <= 0 )' to work as expected - - CCerror.clear(); - - if ( fHelp || params.size() > 4 || params.size() < 2 ) - throw runtime_error("tokencreate name supply [description][data]\n"); - if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - LOCK2(cs_main, pwalletMain->cs_wallet); // remote call not supported yet - - name = params[0].get_str(); - if (name.size() == 0 || name.size() > 32) - return MakeResultError("Token name must not be empty and up to 32 characters"); - - supply = AmountFromValue(params[1]); - if (supply <= 0) - return MakeResultError("Token supply must be positive"); - - - if (params.size() >= 3) { - description = params[2].get_str(); - if (description.size() > 4096) - return MakeResultError("Token description must be <= 4096 characters"); - } - - if (params.size() == 4) { - nonfungibleData = ParseHex(params[3].get_str()); - if (nonfungibleData.size() > IGUANA_MAXSCRIPTSIZE) // opret limit - return MakeResultError("Non-fungible data size must be <= " + std::to_string(IGUANA_MAXSCRIPTSIZE)); - - if( nonfungibleData.empty() ) - return MakeResultError("Non-fungible data incorrect"); - } - - hextx = CreateTokenLocal(0, supply, name, description, nonfungibleData); - RETURN_IF_ERROR(CCerror); - - if( hextx.size() > 0 ) - return MakeResultSuccess(hextx); - else - return MakeResultError("could not create token"); -} - -UniValue tokentransfer(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - std::string hex; - int64_t amount; - uint256 tokenid; - - CCerror.clear(); - - if ( fHelp || params.size() < 3) - throw runtime_error("tokentransfer tokenid destpubkey amount \n"); - if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - LOCK2(cs_main, pwalletMain->cs_wallet); // remote call not supported yet - - tokenid = Parseuint256((char *)params[0].get_str().c_str()); - vuint8_t vpubkey(ParseHex(params[1].get_str().c_str())); - amount = atoll(params[2].get_str().c_str()); - if( tokenid == zeroid ) - return MakeResultError("invalid tokenid"); - if (vpubkey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) - return MakeResultError("invalid destpubkey"); - if( amount <= 0 ) - return MakeResultError("amount must be positive"); - - hex = TokenTransfer(0, tokenid, pubkey2pk(vpubkey), amount); - RETURN_IF_ERROR(CCerror); - if (hex.size() > 0) - return MakeResultSuccess(hex); - else - return MakeResultError("could not transfer token"); -} - -UniValue tokentransfermany(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - std::string hex; - CAmount amount; - uint256 tokenid; - const CAmount txfee = 10000; - - CCerror.clear(); - - if ( fHelp || params.size() < 3) - throw runtime_error("tokentransfermany tokenid1 tokenid2 ... destpubkey amount \n"); - if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - - std::vector tokenids; - int i = 0; - for (; i < params.size() - 2; i ++) - { - uint256 tokenid = Parseuint256((char *)params[i].get_str().c_str()); - if( tokenid == zeroid ) - return MakeResultError("invalid tokenid"); - tokenids.push_back(tokenid); - } - CPubKey destpk = pubkey2pk(ParseHex(params[i++].get_str().c_str())); - if (destpk.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) - return MakeResultError("invalid destpubkey"); - - amount = atoll(params[i].get_str().c_str()); - if( amount <= 0 ) - return MakeResultError("amount must be positive"); - - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - LOCK2(cs_main, pwalletMain->cs_wallet); // remote call not supported yet - - CPubKey mypk = remotepk.IsValid() ? remotepk : pubkey2pk(Mypubkey()); - - CMutableTransaction mtx; - struct CCcontract_info *cpTokens, CTokens; - cpTokens = CCinit(&CTokens, EVAL_TOKENS); - - UniValue beginResult = TokenBeginTransferTx(mtx, cpTokens, remotepk, txfee); - if (ResultIsError(beginResult)) - return beginResult; - - for (const auto &tokenid : tokenids) - { - vuint8_t vnftData; - GetNonfungibleData(tokenid, vnftData); - CC* probeCond; - if (vnftData.size() > 0) - probeCond = MakeTokensCCcond1(vnftData[0], mypk); - else - probeCond = MakeCCcond1(EVAL_TOKENS, mypk); - - uint8_t mypriv[32]; - Myprivkey(mypriv); - - char tokenaddr[KOMODO_ADDRESS_BUFSIZE]; - cpTokens->evalcodeNFT = vnftData.size() > 0 ? vnftData[0] : 0; - GetTokensCCaddress(cpTokens, tokenaddr, mypk); - - UniValue addtxResult = TokenAddTransferVout(mtx, cpTokens, destpk, tokenid, tokenaddr, { destpk }, {probeCond, mypriv}, amount, false); - cc_free(probeCond); - memset(mypriv, '\0', sizeof(mypriv)); - if (ResultIsError(addtxResult)) - return MakeResultError( ResultGetError(addtxResult) + " " + tokenid.GetHex() ); - } - UniValue sigData = TokenFinalizeTransferTx(mtx, cpTokens, remotepk, txfee, CScript()); - RETURN_IF_ERROR(CCerror); - if (ResultHasTx(sigData) > 0) - result = sigData; - else - result = MakeResultError("could not transfer token: " + ResultGetError(sigData) ); - return(result); -} - -UniValue tokenconvert(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); std::string hex; int32_t evalcode; int64_t amount; uint256 tokenid; - CCerror.clear(); - if ( fHelp || params.size() != 4 ) - throw runtime_error("tokenconvert evalcode tokenid pubkey amount\n"); - if ( ensure_CCrequirements(EVAL_ASSETS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - const CKeyStore& keystore = *pwalletMain; - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - LOCK2(cs_main, pwalletMain->cs_wallet); - evalcode = atoi(params[0].get_str().c_str()); - tokenid = Parseuint256((char *)params[1].get_str().c_str()); - std::vector pubkey(ParseHex(params[2].get_str().c_str())); - //amount = atol(params[3].get_str().c_str()); - amount = atoll(params[3].get_str().c_str()); // dimxy changed to prevent loss of significance - if ( tokenid == zeroid ) - { - ERR_RESULT("invalid tokenid"); - return(result); - } - if ( amount <= 0 ) - { - ERR_RESULT("amount must be positive"); - return(result); - } - - ERR_RESULT("deprecated"); - return(result); - -/* hex = AssetConvert(0,tokenid,pubkey,amount,evalcode); - if (amount > 0) { - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } else ERR_RESULT("couldnt convert tokens"); - } else { - ERR_RESULT("amount must be positive"); - } - return(result); */ -} - -UniValue tokenbid(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); int64_t bidamount,numtokens; std::string hex; uint256 tokenid; - - CCerror.clear(); - if ( fHelp || params.size() != 3 ) - throw runtime_error("tokenbid numtokens tokenid price\n"); - if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); - - numtokens = atoll(params[0].get_str().c_str()); - tokenid = Parseuint256((char *)params[1].get_str().c_str()); - CAmount price = AmountFromValue(params[2]); - bidamount = (price * numtokens); - if (price <= 0) - return MakeResultError("price must be positive"); - - if (tokenid == zeroid) - return MakeResultError("invalid tokenid"); - - if (bidamount <= 0) - return MakeResultError("bid amount must be positive"); - - CPubKey mypk; - SET_MYPK_OR_REMOTE(mypk, remotepk); - - if (TokensIsVer1Active(NULL)) - result = CreateBuyOffer(mypk, 0, bidamount, tokenid, numtokens); - else { - hex = tokensv0::CreateBuyOffer(0, bidamount, tokenid, numtokens); - if (!hex.empty()) - result = MakeResultSuccess(hex); - else - result = MakeResultError("could not create bid"); - } - RETURN_IF_ERROR(CCerror); - return(result); -} - -UniValue tokencancelbid(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); std::string hex; int32_t i; uint256 tokenid,bidtxid; - CCerror.clear(); - if (fHelp || params.size() != 2) - throw runtime_error("tokencancelbid tokenid bidtxid\n"); - - if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); - - tokenid = Parseuint256((char *)params[0].get_str().c_str()); - bidtxid = Parseuint256((char *)params[1].get_str().c_str()); - if ( tokenid == zeroid || bidtxid == zeroid ) - return MakeResultError("invalid parameter"); - - CPubKey mypk; - SET_MYPK_OR_REMOTE(mypk, remotepk); - if (TokensIsVer1Active(NULL)) - result = CancelBuyOffer(mypk, 0,tokenid,bidtxid); - else { - hex = tokensv0::CancelBuyOffer(0,tokenid,bidtxid); - if (!hex.empty()) - result = MakeResultSuccess(hex); - else - result = MakeResultError("could not cancel bid"); - } - RETURN_IF_ERROR(CCerror); - return(result); -} - -UniValue tokenfillbid(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - int64_t fillamount; - std::string hex; - uint256 tokenid,bidtxid; - - CCerror.clear(); - - if (fHelp || params.size() != 3 && params.size() != 4) - throw runtime_error("tokenfillbid tokenid bidtxid fillamount [unit_price]\n"); - if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); - - tokenid = Parseuint256((char *)params[0].get_str().c_str()); - bidtxid = Parseuint256((char *)params[1].get_str().c_str()); - fillamount = atoll(params[2].get_str().c_str()); - if (fillamount <= 0) - return MakeResultError("fillamount must be positive"); - - if (tokenid == zeroid || bidtxid == zeroid) - return MakeResultError("must provide tokenid and bidtxid"); - - CAmount unit_price = 0LL; - if (params.size() == 4) - unit_price = AmountFromValue(params[3].get_str().c_str()); - CPubKey mypk; - SET_MYPK_OR_REMOTE(mypk, remotepk); - if (TokensIsVer1Active(NULL)) - result = FillBuyOffer(mypk, 0, tokenid, bidtxid, fillamount, unit_price); - else { - hex = tokensv0::FillBuyOffer(0, tokenid, bidtxid, fillamount); - if (!hex.empty()) - result = MakeResultSuccess(hex); - else - result = MakeResultError("could not fill bid"); - } - RETURN_IF_ERROR(CCerror); - return(result); -} - -UniValue tokenask(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - int64_t askamount, numtokens; - std::string hex; - uint256 tokenid; - - CCerror.clear(); - if (fHelp || params.size() != 3) - throw runtime_error("tokenask numtokens tokenid price\n"); - if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); - - numtokens = atoll(params[0].get_str().c_str()); - tokenid = Parseuint256((char *)params[1].get_str().c_str()); - CAmount price = AmountFromValue(params[2]); - askamount = (price * numtokens); - if (tokenid == zeroid || numtokens <= 0 || price <= 0 || askamount <= 0) - return MakeResultError("invalid parameter"); - - CPubKey mypk; - SET_MYPK_OR_REMOTE(mypk, remotepk); - if (TokensIsVer1Active(NULL)) - result = CreateSell(mypk, 0, numtokens, tokenid, askamount); - else { - hex = tokensv0::CreateSell(0, numtokens, tokenid, askamount); - if (!hex.empty()) - result = MakeResultSuccess(hex); - else - result = MakeResultError("could not create ask"); - } - RETURN_IF_ERROR(CCerror); - return(result); -} - -// not implemented -UniValue tokenswapask(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - static uint256 zeroid; - UniValue result(UniValue::VOBJ); int64_t askamount,numtokens; std::string hex; double price; uint256 tokenid,otherid; - - CCerror.clear(); - if (fHelp || params.size() != 4) - throw runtime_error("tokenswapask numtokens tokenid otherid price\n"); - if (ensure_CCrequirements(EVAL_ASSETS) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); - - throw runtime_error("tokenswapask not supported\n"); - - numtokens = atoll(params[0].get_str().c_str()); - tokenid = Parseuint256((char *)params[1].get_str().c_str()); - otherid = Parseuint256((char *)params[2].get_str().c_str()); - price = atof(params[3].get_str().c_str()); - askamount = (price * numtokens); - hex = CreateSwap(0,numtokens,tokenid,otherid,askamount); - RETURN_IF_ERROR(CCerror); - if (price > 0 && numtokens > 0) { - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } else ERR_RESULT("couldnt create swap"); - } else { - ERR_RESULT("price and numtokens must be positive"); - } - return(result); -} - -UniValue tokencancelask(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); std::string hex; int32_t i; uint256 tokenid,asktxid; - - CCerror.clear(); - if (fHelp || params.size() != 2) - throw runtime_error("tokencancelask tokenid asktxid\n"); - - if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); - - tokenid = Parseuint256((char *)params[0].get_str().c_str()); - asktxid = Parseuint256((char *)params[1].get_str().c_str()); - if (tokenid == zeroid || asktxid == zeroid) - return MakeResultError("invalid parameter"); - - CPubKey mypk; - SET_MYPK_OR_REMOTE(mypk, remotepk); - if (TokensIsVer1Active(NULL)) - result = CancelSell(mypk, 0, tokenid, asktxid); - else { - hex = tokensv0::CancelSell(0, tokenid, asktxid); - if (!hex.empty()) - result = MakeResultSuccess(hex); - else - result = MakeResultError("could not cancel ask"); - } - RETURN_IF_ERROR(CCerror); - return(result); -} - -UniValue tokenfillask(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - UniValue result(UniValue::VOBJ); - int64_t fillunits; - std::string hex; - uint256 tokenid,asktxid; - - if (fHelp || params.size() != 3 && params.size() != 4) - throw runtime_error("tokenfillask tokenid asktxid fillunits [unitprice]\n"); - if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); - - tokenid = Parseuint256((char *)params[0].get_str().c_str()); - asktxid = Parseuint256((char *)params[1].get_str().c_str()); - fillunits = atoll(params[2].get_str().c_str()); - if (fillunits <= 0) - return MakeResultError("fillunits must be positive"); - if (tokenid == zeroid || asktxid == zeroid) - return MakeResultError("invalid parameter"); - CAmount unit_price = 0LL; - if (params.size() == 4) - unit_price = AmountFromValue(params[3].get_str().c_str()); - - CPubKey mypk; - SET_MYPK_OR_REMOTE(mypk, remotepk); - if (TokensIsVer1Active(NULL)) - result = FillSell(mypk, 0, tokenid, zeroid, asktxid, fillunits, unit_price); - else { - hex = tokensv0::FillSell(0, tokenid, zeroid, asktxid, fillunits); - if (!hex.empty()) - result = MakeResultSuccess(hex); - else - result = MakeResultError("could not fill ask"); - } - RETURN_IF_ERROR(CCerror); - return(result); -} - -// not used yet -UniValue tokenfillswap(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - static uint256 zeroid; - UniValue result(UniValue::VOBJ); - int64_t fillunits; std::string hex; uint256 tokenid,otherid,asktxid; - - CCerror.clear(); - if (fHelp || params.size() != 4 && params.size() != 5) - throw runtime_error("tokenfillswap tokenid otherid asktxid fillunits [unitprice]\n"); - if (ensure_CCrequirements(EVAL_ASSETS) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - if (!EnsureWalletIsAvailable(false)) - throw runtime_error("wallet is required"); - CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); - - throw runtime_error("tokenfillswap not supported\n"); - - tokenid = Parseuint256((char *)params[0].get_str().c_str()); - otherid = Parseuint256((char *)params[1].get_str().c_str()); - asktxid = Parseuint256((char *)params[2].get_str().c_str()); - //fillunits = atol(params[3].get_str().c_str()); - fillunits = atoll(params[3].get_str().c_str()); // dimxy changed to prevent loss of significance - CAmount unit_price = 0LL; - if (params.size() == 5) - unit_price = AmountFromValue(params[4].get_str().c_str()); - CPubKey mypk; - SET_MYPK_OR_REMOTE(mypk, remotepk); - result = FillSell(mypk,0,tokenid,otherid,asktxid,fillunits, unit_price); - RETURN_IF_ERROR(CCerror); - if (fillunits > 0) { - if ( hex.size() > 0 ) { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } else ERR_RESULT("couldnt fill bid"); - } else { - ERR_RESULT("fillunits must be positive"); - } - return(result); -} - - -static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode - // -------------- ------------------------ ----------------------- ---------- - // Marmara - // tokens & assets - { "tokens", "assetsaddress", &assetsaddress, true }, - { "tokens", "tokeninfo", &tokeninfo, true }, - { "tokens", "tokenlist", &tokenlist, true }, - { "tokens", "tokenorders", &tokenorders, true }, - { "tokens", "mytokenorders", &mytokenorders, true }, - { "tokens", "tokenaddress", &tokenaddress, true }, - { "tokens", "tokenbalance", &tokenbalance, true }, - { "tokens", "tokencreate", &tokencreate, true }, - { "tokens", "tokentransfer", &tokentransfer, true }, - { "tokens", "tokentransfermany", &tokentransfermany, true }, - { "tokens", "tokenbid", &tokenbid, true }, - { "tokens", "tokencancelbid", &tokencancelbid, true }, - { "tokens", "tokenfillbid", &tokenfillbid, true }, - { "tokens", "tokenask", &tokenask, true }, - //{ "tokens", "tokenswapask", &tokenswapask, true }, - { "tokens", "tokencancelask", &tokencancelask, true }, - { "tokens", "tokenfillask", &tokenfillask, true }, - //{ "tokens", "tokenfillswap", &tokenfillswap, true }, - { "tokens", "tokenconvert", &tokenconvert, true }, -}; - -void RegisterTokensRPCCommands(CRPCTable &tableRPC) -{ - for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) - tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); -} diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 0cf285af764..e964609df7d 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -246,9 +246,6 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip } else { - // NOTE: SignStepCC is not currently used in cc modules (we use FinalizeCCTx which signs cc vins itself) - // but if we try to use SignStepCC with a spk having a cc opret - // this won't work because this parsing constructor COptCCParams wont parse without pubkey(s) in the cc opret but we do not add them when a cc vout is made p = COptCCParams(vParams[0]); } diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 47dd77a0576..ee681642c6a 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -34,8 +34,7 @@ typedef vector valtype; unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY; -// warning: this parsing constructor won't work because it expects pubkeys (vKeys var) but we do not add pubkeys in MakeCC..vout functions -COptCCParams::COptCCParams(std::vector &vch) +COptCCParams::COptCCParams(std::vector &vch) { CScript inScr = CScript(vch.begin(), vch.end()); if (inScr.size() > 1) @@ -245,18 +244,17 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector(k.begin(), k.end())); - // } - // } - // } + if (vParams.size()) + { + COptCCParams cp = COptCCParams(vParams[0]); + if (cp.IsValid()) + { + for (auto k : cp.vKeys) + { + vSolutionsRet.push_back(std::vector(k.begin(), k.end())); + } + } + } return true; } return false; @@ -384,9 +382,19 @@ bool ExtractDestination(const CScript& _scriptPubKey, CTxDestination& addressRet addressRet = CScriptID(uint160(vSolutions[0])); return true; } + else if (IsCryptoConditionsEnabled() != 0 && whichType == TX_CRYPTOCONDITION) { - addressRet = CKeyID(uint160(vSolutions[0])); + if (vSolutions.size() > 1) + { + CPubKey pk = CPubKey((vSolutions[1])); + addressRet = pk; + return pk.IsValid(); + } + else + { + addressRet = CKeyID(uint160(vSolutions[0])); + } return true; } // Multisig txns have more than one address... @@ -435,6 +443,27 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto if (addressRet.empty()) return false; } + // Removed to get CC address printed in getrawtransaction and decoderawtransaction + // else if (IsCryptoConditionsEnabled() != 0 && typeRet == TX_CRYPTOCONDITION) + // { + // nRequiredRet = vSolutions.front()[0]; + // for (unsigned int i = 1; i < vSolutions.size()-1; i++) + // { + // CTxDestination address; + // if (vSolutions[i].size() == 20) + // { + // address = CKeyID(uint160(vSolutions[i])); + // } + // else + // { + // address = CPubKey(vSolutions[i]); + // } + // addressRet.push_back(address); + // } + + // if (addressRet.empty()) + // return false; + // } else { nRequiredRet = 1; diff --git a/src/sync_ext.h b/src/sync_ext.h deleted file mode 100644 index 7b460532391..00000000000 --- a/src/sync_ext.h +++ /dev/null @@ -1,82 +0,0 @@ -/****************************************************************************** -* Copyright © 2014-2019 The SuperNET Developers. * -* * -* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * -* the top-level directory of this distribution for the individual copyright * -* holder information and the developer policies on copyright and licensing. * -* * -* Unless otherwise agreed in a custom licensing agreement, no part of the * -* SuperNET software, including this file may be copied, modified, propagated * -* or distributed except according to the terms contained in the LICENSE file * -* * -* Removal or modification of this copyright notice is prohibited. * -* * -******************************************************************************/ - -#ifndef __SYNC_EXT_H__ -#define __SYNC_EXT_H__ - -#include "cc/CCinclude.h" -#include "sync.h" - -// sync extension - -/* conditional lock */ -template -class SCOPED_LOCKABLE CMutexLockConditional -{ -private: - boost::unique_lock lock; - - void Enter(const char* pszName, const char* pszFile, int nLine) - { - EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); -#ifdef DEBUG_LOCKCONTENTION - if (!lock.try_lock()) { - PrintLockContention(pszName, pszFile, nLine); -#endif - lock.lock(); -#ifdef DEBUG_LOCKCONTENTION - } -#endif - } - -public: - CMutexLockConditional(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool doLock) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, boost::defer_lock) - { - if (doLock) { - Enter(pszName, pszFile, nLine); - if (lock.owns_lock()) - LOGSTREAMFN("lock-conditional", CCLOG_DEBUG1, stream << "cs=" << pszName << " file=" << pszFile << " nLine=" << nLine << " entered, locked" << std::endl); - } - } - - CMutexLockConditional(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool doLock) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) - { - if (!pmutexIn) return; - - lock = boost::unique_lock(*pmutexIn, boost::defer_lock); - if (doLock) - Enter(pszName, pszFile, nLine); - } - - ~CMutexLockConditional() UNLOCK_FUNCTION() - { - if (lock.owns_lock()) { - LeaveCritical(); - LOGSTREAMFN("lock-conditional", CCLOG_DEBUG1, stream << "exited, was locked" << std::endl); - } - } - - operator bool() - { - return lock.owns_lock(); - } -}; - -typedef CMutexLockConditional CCriticalBlockConditional; - -#define CONDITIONAL_LOCK(cs, flag) CCriticalBlockConditional criticalblock(cs, #cs, __FILE__, __LINE__, flag) -#define CONDITIONAL_LOCK2(cs1, cs2, flag) CCriticalBlockConditional criticalblock1(cs1, #cs1, __FILE__, __LINE__, flag), criticalblock2(cs2, #cs2, __FILE__, __LINE__, flag) - -#endif \ No newline at end of file diff --git a/src/test-komodo/test_addrman.cpp b/src/test-komodo/test_addrman.cpp new file mode 100644 index 00000000000..f283b971090 --- /dev/null +++ b/src/test-komodo/test_addrman.cpp @@ -0,0 +1,862 @@ +#include + +#include "addrman.h" +#include +#include +#include + +#include "hash.h" +#include "random.h" +#include "util/asmap.h" + +#include "netbase.h" +#include "chainparams.h" +#include "tinyformat.h" +#include "utilstrencodings.h" + +#define NODE_NONE 0 + +// https://stackoverflow.com/questions/16491675/how-to-send-custom-message-in-google-c-testing-framework/29155677 +#define GTEST_COUT_NOCOLOR std::cerr << "[ ] [ INFO ] " +namespace testing +{ + namespace internal + { + enum GTestColor { + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW + }; + + extern void ColoredPrintf(GTestColor color, const char* fmt, ...); + } +} +#define PRINTF(...) do { testing::internal::ColoredPrintf(testing::internal::COLOR_GREEN, "[ ] "); testing::internal::ColoredPrintf(testing::internal::COLOR_YELLOW, __VA_ARGS__); } while(0) + +// C++ stream interface +class TestCout : public std::stringstream +{ + public: + ~TestCout() + { + PRINTF("%s",str().c_str()); + } +}; + +#define GTEST_COUT_COLOR TestCout() + +using namespace std; + +/* xxd -i est-komodo/data/asmap.raw | sed 's/unsigned char/static unsigned const char/g' */ +static unsigned const char asmap_raw[] = { + 0xfb, 0x03, 0xec, 0x0f, 0xb0, 0x3f, 0xc0, 0xfe, 0x00, 0xfb, 0x03, 0xec, + 0x0f, 0xb0, 0x3f, 0xc0, 0xfe, 0x00, 0xfb, 0x03, 0xec, 0x0f, 0xb0, 0xff, + 0xff, 0xfe, 0xff, 0xed, 0xb0, 0xff, 0xd4, 0x86, 0xe6, 0x28, 0x29, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x40, 0x99, 0x01, 0x00, 0x80, 0x01, + 0x80, 0x04, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1c, 0xf0, 0x39 +}; +unsigned int asmap_raw_len = 59; + +class CAddrManTest : public CAddrMan +{ + private: + uint64_t state; + bool deterministic; + public: + + explicit CAddrManTest(bool makeDeterministic = true, + std::vector asmap = std::vector()) + { + if (makeDeterministic) { + // Set addrman addr placement to be deterministic. + MakeDeterministic(); + } + deterministic = makeDeterministic; + m_asmap = asmap; + state = 1; + } + + void PrintInternals() + { + GTEST_COUT_NOCOLOR << "mapInfo.size() = " << mapInfo.size() << std::endl; + GTEST_COUT_NOCOLOR << "nNew = " << nNew << std::endl; + } + + //! Ensure that bucket placement is always the same for testing purposes. + void MakeDeterministic() + { + nKey.SetNull(); + seed_insecure_rand(true); + } + + int RandomInt(int nMax) + { + state = (CHashWriter(SER_GETHASH, 0) << state).GetHash().GetCheapHash(); + return (unsigned int)(state % nMax); + } + + CAddrInfo* Find(const CNetAddr& addr, int* pnId = NULL) + { + return CAddrMan::Find(addr, pnId); + } + + CAddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = NULL) + { + return CAddrMan::Create(addr, addrSource, pnId); + } + + void Delete(int nId) + { + CAddrMan::Delete(nId); + } + + // Used to test deserialization + std::pair GetBucketAndEntry(const CAddress& addr) + { + // LOCK(cs); + int nId = mapAddr[addr]; + for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) { + for (int entry = 0; entry < ADDRMAN_BUCKET_SIZE; ++entry) { + if (nId == vvNew[bucket][entry]) { + return std::pair(bucket, entry); + } + } + } + return std::pair(-1, -1); + } + + void Clear() + { + CAddrMan::Clear(); + if (deterministic) { + nKey.SetNull(); + seed_insecure_rand(true); + } + } +}; + +static CNetAddr ResolveIP(const std::string& ip) +{ + vector vIPs; + CNetAddr addr; + if (LookupHost(ip.c_str(), vIPs)) { + addr = vIPs[0]; + } else + { + // it was BOOST_CHECK_MESSAGE, but we can't use ASSERT or EXPECT outside a test + GTEST_COUT_COLOR << strprintf("failed to resolve: %s", ip) << std::endl; + } + return addr; +} + +static CService ResolveService(const std::string& ip, const int port = 0) +{ + CService serv; + if (!Lookup(ip.c_str(), serv, port, false)) + GTEST_COUT_COLOR << strprintf("failed to resolve: %s:%i", ip, port) << std::endl; + return serv; +} + +static std::vector FromBytes(const unsigned char* source, int vector_size) { + std::vector result(vector_size); + for (int byte_i = 0; byte_i < vector_size / 8; ++byte_i) { + unsigned char cur_byte = source[byte_i]; + for (int bit_i = 0; bit_i < 8; ++bit_i) { + result[byte_i * 8 + bit_i] = (cur_byte >> bit_i) & 1; + } + } + return result; +} + +namespace TestAddrmanTests { + + TEST(TestAddrmanTests, display_constants) { + + // Not actually the test, just used to display constants + GTEST_COUT_COLOR << "ADDRMAN_NEW_BUCKET_COUNT = " << ADDRMAN_NEW_BUCKET_COUNT << std::endl; + GTEST_COUT_COLOR << "ADDRMAN_TRIED_BUCKET_COUNT = " << ADDRMAN_TRIED_BUCKET_COUNT << std::endl; + GTEST_COUT_COLOR << "ADDRMAN_BUCKET_SIZE = " << ADDRMAN_BUCKET_SIZE << std::endl; + + } + + TEST(TestAddrmanTests, addrman_simple) { + + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + CNetAddr source = CNetAddr("252.2.2.2"); + + // Test 1: Does Addrman respond correctly when empty. + ASSERT_TRUE(addrman.size() == 0); + CAddrInfo addr_null = addrman.Select(); + ASSERT_TRUE(addr_null.ToString() == "[::]:0"); + + // Test 2: Does Addrman::Add work as expected. + CService addr1 = CService("250.1.1.1", 8333); + addrman.Add(CAddress(addr1, NODE_NONE), source); + ASSERT_TRUE(addrman.size() == 1); + CAddrInfo addr_ret1 = addrman.Select(); + ASSERT_TRUE(addr_ret1.ToString() == "250.1.1.1:8333"); + + // Test 3: Does IP address deduplication work correctly. + // Expected dup IP should not be added. + CService addr1_dup = CService("250.1.1.1", 8333); + addrman.Add(CAddress(addr1_dup, NODE_NONE), source); + ASSERT_TRUE(addrman.size() == 1); + + // Test 5: New table has one addr and we add a diff addr we should + // have two addrs. + CService addr2 = CService("250.1.1.2", 8333); + addrman.Add(CAddress(addr2, NODE_NONE), source); + ASSERT_TRUE(addrman.size() == 2); + + // Test 6: AddrMan::Clear() should empty the new table. + addrman.Clear(); + ASSERT_TRUE(addrman.size() == 0); + CAddrInfo addr_null2 = addrman.Select(); + ASSERT_TRUE(addr_null2.ToString() == "[::]:0"); + + } + + TEST(TestAddrmanTests, addrman_ports) { + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + CNetAddr source = CNetAddr("252.2.2.2"); + + ASSERT_TRUE(addrman.size() == 0); + + // Test 7; Addr with same IP but diff port does not replace existing addr. + CService addr1 = CService("250.1.1.1", 8333); + addrman.Add(CAddress(addr1, NODE_NONE), source); + ASSERT_TRUE(addrman.size() == 1); + + CService addr1_port = CService("250.1.1.1", 8334); + addrman.Add(CAddress(addr1_port, NODE_NONE), source); + ASSERT_TRUE(addrman.size() == 1); + CAddrInfo addr_ret2 = addrman.Select(); + ASSERT_TRUE(addr_ret2.ToString() == "250.1.1.1:8333"); + + // Test 8: Add same IP but diff port to tried table, it doesn't get added. + // Perhaps this is not ideal behavior but it is the current behavior. + addrman.Good(CAddress(addr1_port, NODE_NONE)); + ASSERT_TRUE(addrman.size() == 1); + bool newOnly = true; + CAddrInfo addr_ret3 = addrman.Select(newOnly); + ASSERT_TRUE(addr_ret3.ToString() == "250.1.1.1:8333"); + + } + + TEST(TestAddrmanTests, addrman_select) { + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + CNetAddr source = CNetAddr("252.2.2.2"); + + // Test 9: Select from new with 1 addr in new. + CService addr1 = CService("250.1.1.1", 8333); + addrman.Add(CAddress(addr1, NODE_NONE), source); + ASSERT_TRUE(addrman.size() == 1); + + bool newOnly = true; + CAddrInfo addr_ret1 = addrman.Select(newOnly); + ASSERT_TRUE(addr_ret1.ToString() == "250.1.1.1:8333"); + + // Test 10: move addr to tried, select from new expected nothing returned. + addrman.Good(CAddress(addr1, NODE_NONE)); + ASSERT_TRUE(addrman.size() == 1); + CAddrInfo addr_ret2 = addrman.Select(newOnly); + ASSERT_TRUE(addr_ret2.ToString() == "[::]:0"); + + CAddrInfo addr_ret3 = addrman.Select(); + ASSERT_TRUE(addr_ret3.ToString() == "250.1.1.1:8333"); + + ASSERT_TRUE(addrman.size() == 1); + + + // Add three addresses to new table. + CService addr2 = CService("250.3.1.1", 8333); + CService addr3 = CService("250.3.2.2", 9999); + CService addr4 = CService("250.3.3.3", 9999); + + addrman.Add(CAddress(addr2, NODE_NONE), CService("250.3.1.1", 8333)); + addrman.Add(CAddress(addr3, NODE_NONE), CService("250.3.1.1", 8333)); + addrman.Add(CAddress(addr4, NODE_NONE), CService("250.4.1.1", 8333)); + + // Add three addresses to tried table. + CService addr5 = CService("250.4.4.4", 8333); + CService addr6 = CService("250.4.5.5", 7777); + CService addr7 = CService("250.4.6.6", 8333); + + addrman.Add(CAddress(addr5, NODE_NONE), CService("250.3.1.1", 8333)); + addrman.Good(CAddress(addr5, NODE_NONE)); + addrman.Add(CAddress(addr6, NODE_NONE), CService("250.3.1.1", 8333)); + addrman.Good(CAddress(addr6, NODE_NONE)); + addrman.Add(CAddress(addr7, NODE_NONE), CService("250.1.1.3", 8333)); + addrman.Good(CAddress(addr7, NODE_NONE)); + + // Test 11: 6 addrs + 1 addr from last test = 7. + ASSERT_TRUE(addrman.size() == 7); + + // Test 12: Select pulls from new and tried regardless of port number. + ASSERT_TRUE(addrman.Select().ToString() == "250.4.6.6:8333"); + ASSERT_TRUE(addrman.Select().ToString() == "250.3.2.2:9999"); + ASSERT_TRUE(addrman.Select().ToString() == "250.3.3.3:9999"); + ASSERT_TRUE(addrman.Select().ToString() == "250.4.4.4:8333"); + + } + + TEST(TestAddrmanTests, addrman_new_collisions) + { + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + CNetAddr source = CNetAddr("252.2.2.2"); + + ASSERT_TRUE(addrman.size() == 0); + + for (unsigned int i = 1; i < 18; i++) { + CService addr = CService("250.1.1." + boost::to_string(i)); + addrman.Add(CAddress(addr, NODE_NONE), source); + //Test 13: No collision in new table yet. + ASSERT_TRUE(addrman.size() == i); + } + + //Test 14: new table collision! + CService addr1 = CService("250.1.1.18"); + addrman.Add(CAddress(addr1, NODE_NONE), source); + ASSERT_TRUE(addrman.size() == 17); + + CService addr2 = CService("250.1.1.19"); + addrman.Add(CAddress(addr2, NODE_NONE), source); + ASSERT_TRUE(addrman.size() == 18); + } + + TEST(TestAddrmanTests, addrman_tried_collisions) + { + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + CNetAddr source = CNetAddr("252.2.2.2"); + + ASSERT_TRUE(addrman.size() == 0); + + for (unsigned int i = 1; i < 80; i++) { + CService addr = CService("250.1.1." + boost::to_string(i)); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(CAddress(addr, NODE_NONE)); + + //Test 15: No collision in tried table yet. + // GTEST_COUT << addrman.size() << std::endl; + ASSERT_TRUE(addrman.size() == i); + } + + //Test 16: tried table collision! + CService addr1 = CService("250.1.1.80"); + addrman.Add(CAddress(addr1, NODE_NONE), source); + ASSERT_TRUE(addrman.size() == 79); + + CService addr2 = CService("250.1.1.81"); + addrman.Add(CAddress(addr2, NODE_NONE), source); + ASSERT_TRUE(addrman.size() == 80); + } + + TEST(TestAddrmanTests, addrman_find) + { + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + ASSERT_TRUE(addrman.size() == 0); + + CAddress addr1 = CAddress(CService("250.1.2.1", 8333), NODE_NONE); + CAddress addr2 = CAddress(CService("250.1.2.1", 9999), NODE_NONE); + CAddress addr3 = CAddress(CService("251.255.2.1", 8333), NODE_NONE); + + CNetAddr source1 = CNetAddr("250.1.2.1"); + CNetAddr source2 = CNetAddr("250.1.2.2"); + + addrman.Add(addr1, source1); + addrman.Add(addr2, source2); + addrman.Add(addr3, source1); + + // Test 17: ensure Find returns an IP matching what we searched on. + CAddrInfo* info1 = addrman.Find(addr1); + ASSERT_TRUE(info1); + if (info1) + ASSERT_TRUE(info1->ToString() == "250.1.2.1:8333"); + + // Test 18; Find does not discriminate by port number. + CAddrInfo* info2 = addrman.Find(addr2); + ASSERT_TRUE(info2); + if (info2) + ASSERT_TRUE(info2->ToString() == info1->ToString()); + + // Test 19: Find returns another IP matching what we searched on. + CAddrInfo* info3 = addrman.Find(addr3); + ASSERT_TRUE(info3); + if (info3) + ASSERT_TRUE(info3->ToString() == "251.255.2.1:8333"); + } + + TEST(TestAddrmanTests, addrman_create) + { + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + ASSERT_TRUE(addrman.size() == 0); + + CAddress addr1 = CAddress(CService("250.1.2.1", 8333), NODE_NONE); + CNetAddr source1 = CNetAddr("250.1.2.1"); + + int nId; + CAddrInfo* pinfo = addrman.Create(addr1, source1, &nId); + + // Test 20: The result should be the same as the input addr. + ASSERT_TRUE(pinfo->ToString() == "250.1.2.1:8333"); + + CAddrInfo* info2 = addrman.Find(addr1); + ASSERT_TRUE(info2->ToString() == "250.1.2.1:8333"); + } + + + TEST(TestAddrmanTests, addrman_delete) + { + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + ASSERT_TRUE(addrman.size() == 0); + + CAddress addr1 = CAddress(CService("250.1.2.1", 8333), NODE_NONE); + CNetAddr source1 = CNetAddr("250.1.2.1"); + + int nId; + addrman.Create(addr1, source1, &nId); + + // Test 21: Delete should actually delete the addr. + ASSERT_TRUE(addrman.size() == 1); + addrman.Delete(nId); + ASSERT_TRUE(addrman.size() == 0); + CAddrInfo* info2 = addrman.Find(addr1); + ASSERT_TRUE(info2 == NULL); + } + + TEST(TestAddrmanTests, addrman_getaddr) + { + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + // Test 22: Sanity check, GetAddr should never return anything if addrman + // is empty. + ASSERT_TRUE(addrman.size() == 0); + vector vAddr1 = addrman.GetAddr(); + ASSERT_TRUE(vAddr1.size() == 0); + + CAddress addr1 = CAddress(CService("250.250.2.1", 8333), NODE_NONE); + addr1.nTime = GetTime(); // Set time so isTerrible = false + CAddress addr2 = CAddress(CService("250.251.2.2", 9999), NODE_NONE); + addr2.nTime = GetTime(); + CAddress addr3 = CAddress(CService("251.252.2.3", 8333), NODE_NONE); + addr3.nTime = GetTime(); + CAddress addr4 = CAddress(CService("252.253.3.4", 8333), NODE_NONE); + addr4.nTime = GetTime(); + CAddress addr5 = CAddress(CService("252.254.4.5", 8333), NODE_NONE); + addr5.nTime = GetTime(); + CNetAddr source1 = CNetAddr("250.1.2.1"); + CNetAddr source2 = CNetAddr("250.2.3.3"); + + // Test 23: Ensure GetAddr works with new addresses. + addrman.Add(addr1, source1); + addrman.Add(addr2, source2); + addrman.Add(addr3, source1); + addrman.Add(addr4, source2); + addrman.Add(addr5, source1); + + // GetAddr returns 23% of addresses, 23% of 5 is 1 rounded down. + ASSERT_TRUE(addrman.GetAddr().size() == 1); + + // Test 24: Ensure GetAddr works with new and tried addresses. + addrman.Good(CAddress(addr1, NODE_NONE)); + addrman.Good(CAddress(addr2, NODE_NONE)); + ASSERT_TRUE(addrman.GetAddr().size() == 1); + + // Test 25: Ensure GetAddr still returns 23% when addrman has many addrs. + for (unsigned int i = 1; i < (8 * 256); i++) { + int octet1 = i % 256; + int octet2 = (i / 256) % 256; + int octet3 = (i / (256 * 2)) % 256; + string strAddr = boost::to_string(octet1) + "." + boost::to_string(octet2) + "." + boost::to_string(octet3) + ".23"; + CAddress addr = CAddress(CService(strAddr), NODE_NONE); + + // Ensure that for all addrs in addrman, isTerrible == false. + addr.nTime = GetTime(); + addrman.Add(addr, CNetAddr(strAddr)); + if (i % 8 == 0) + addrman.Good(addr); + } + vector vAddr = addrman.GetAddr(); + + size_t percent23 = (addrman.size() * 23) / 100; + ASSERT_TRUE(vAddr.size() == percent23); + ASSERT_TRUE(vAddr.size() == 461); + // (Addrman.size() < number of addresses added) due to address collisons. + ASSERT_TRUE(addrman.size() == 2007); + } + + TEST(TestAddrmanTests, caddrinfo_get_tried_bucket_legacy) + { + CAddrManTest addrman; + + CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE); + CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE); + + CNetAddr source1 = ResolveIP("250.1.1.1"); + + CAddrInfo info1 = CAddrInfo(addr1, source1); + + uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash(); + uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash(); + + std::vector asmap; // use /16 + + ASSERT_EQ(info1.GetTriedBucket(nKey1, asmap), 40); + + // Test: Make sure key actually randomizes bucket placement. A fail on + // this test could be a security issue. + ASSERT_TRUE(info1.GetTriedBucket(nKey1, asmap) != info1.GetTriedBucket(nKey2, asmap)); + + // Test: Two addresses with same IP but different ports can map to + // different buckets because they have different keys. + CAddrInfo info2 = CAddrInfo(addr2, source1); + + ASSERT_TRUE(info1.GetKey() != info2.GetKey()); + ASSERT_TRUE(info1.GetTriedBucket(nKey1, asmap) != info2.GetTriedBucket(nKey1, asmap)); + + std::set buckets; + for (int i = 0; i < 255; i++) { + CAddrInfo infoi = CAddrInfo( + CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE), + ResolveIP("250.1.1." + boost::to_string(i))); + int bucket = infoi.GetTriedBucket(nKey1, asmap); + buckets.insert(bucket); + } + // Test: IP addresses in the same /16 prefix should + // never get more than 8 buckets with legacy grouping + ASSERT_EQ(buckets.size(), 8U); + + buckets.clear(); + for (int j = 0; j < 255; j++) { + CAddrInfo infoj = CAddrInfo( + CAddress(ResolveService("250." + boost::to_string(j) + ".1.1"), NODE_NONE), + ResolveIP("250." + boost::to_string(j) + ".1.1")); + int bucket = infoj.GetTriedBucket(nKey1, asmap); + buckets.insert(bucket); + } + // Test: IP addresses in the different /16 prefix should map to more than + // 8 buckets with legacy grouping + ASSERT_EQ(buckets.size(), 160U); + } + + TEST(TestAddrmanTests, caddrinfo_get_new_bucket_legacy) + { + CAddrManTest addrman; + + CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); + CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE); + + CNetAddr source1 = ResolveIP("250.1.2.1"); + + CAddrInfo info1 = CAddrInfo(addr1, source1); + + uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash(); + uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash(); + + std::vector asmap; // use /16 + + // Test: Make sure the buckets are what we expect + ASSERT_EQ(info1.GetNewBucket(nKey1, asmap), 786); + ASSERT_EQ(info1.GetNewBucket(nKey1, source1, asmap), 786); + + // Test: Make sure key actually randomizes bucket placement. A fail on + // this test could be a security issue. + ASSERT_TRUE(info1.GetNewBucket(nKey1, asmap) != info1.GetNewBucket(nKey2, asmap)); + + // Test: Ports should not affect bucket placement in the addr + CAddrInfo info2 = CAddrInfo(addr2, source1); + ASSERT_TRUE(info1.GetKey() != info2.GetKey()); + ASSERT_EQ(info1.GetNewBucket(nKey1, asmap), info2.GetNewBucket(nKey1, asmap)); + + std::set buckets; + for (int i = 0; i < 255; i++) { + CAddrInfo infoi = CAddrInfo( + CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE), + ResolveIP("250.1.1." + boost::to_string(i))); + int bucket = infoi.GetNewBucket(nKey1, asmap); + buckets.insert(bucket); + } + // Test: IP addresses in the same group (\16 prefix for IPv4) should + // always map to the same bucket. + ASSERT_EQ(buckets.size(), 1U); + + buckets.clear(); + for (int j = 0; j < 4 * 255; j++) { + CAddrInfo infoj = CAddrInfo(CAddress( + ResolveService( + boost::to_string(250 + (j / 255)) + "." + boost::to_string(j % 256) + ".1.1"), NODE_NONE), + ResolveIP("251.4.1.1")); + int bucket = infoj.GetNewBucket(nKey1, asmap); + buckets.insert(bucket); + } + // Test: IP addresses in the same source groups should map to NO MORE + // than 64 buckets. + ASSERT_TRUE(buckets.size() <= 64); + + buckets.clear(); + for (int p = 0; p < 255; p++) { + CAddrInfo infoj = CAddrInfo( + CAddress(ResolveService("250.1.1.1"), NODE_NONE), + ResolveIP("250." + boost::to_string(p) + ".1.1")); + int bucket = infoj.GetNewBucket(nKey1, asmap); + buckets.insert(bucket); + } + // Test: IP addresses in the different source groups should map to MORE + // than 64 buckets. + ASSERT_TRUE(buckets.size() > 64); + + } + + // The following three test cases use asmap_raw[] from asmap.raw file + // We use an artificial minimal mock mapping + // 250.0.0.0/8 AS1000 + // 101.1.0.0/16 AS1 + // 101.2.0.0/16 AS2 + // 101.3.0.0/16 AS3 + // 101.4.0.0/16 AS4 + // 101.5.0.0/16 AS5 + // 101.6.0.0/16 AS6 + // 101.7.0.0/16 AS7 + // 101.8.0.0/16 AS8 + + TEST(TestAddrmanTests, caddrinfo_get_tried_bucket) + { + CAddrManTest addrman; + + CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE); + CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE); + + CNetAddr source1 = ResolveIP("250.1.1.1"); + + + CAddrInfo info1 = CAddrInfo(addr1, source1); + + uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash(); + uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash(); + + std::vector asmap = FromBytes(asmap_raw, sizeof(asmap_raw) * 8); + + ASSERT_EQ(info1.GetTriedBucket(nKey1, asmap), 236); + + // Test: Make sure key actually randomizes bucket placement. A fail on + // this test could be a security issue. + ASSERT_TRUE(info1.GetTriedBucket(nKey1, asmap) != info1.GetTriedBucket(nKey2, asmap)); + + // Test: Two addresses with same IP but different ports can map to + // different buckets because they have different keys. + CAddrInfo info2 = CAddrInfo(addr2, source1); + + ASSERT_TRUE(info1.GetKey() != info2.GetKey()); + ASSERT_TRUE(info1.GetTriedBucket(nKey1, asmap) != info2.GetTriedBucket(nKey1, asmap)); + + std::set buckets; + for (int j = 0; j < 255; j++) { + CAddrInfo infoj = CAddrInfo( + CAddress(ResolveService("101." + boost::to_string(j) + ".1.1"), NODE_NONE), + ResolveIP("101." + boost::to_string(j) + ".1.1")); + int bucket = infoj.GetTriedBucket(nKey1, asmap); + buckets.insert(bucket); + } + // Test: IP addresses in the different /16 prefix MAY map to more than + // 8 buckets. + ASSERT_TRUE(buckets.size() > 8); + + buckets.clear(); + for (int j = 0; j < 255; j++) { + CAddrInfo infoj = CAddrInfo( + CAddress(ResolveService("250." + boost::to_string(j) + ".1.1"), NODE_NONE), + ResolveIP("250." + boost::to_string(j) + ".1.1")); + int bucket = infoj.GetTriedBucket(nKey1, asmap); + buckets.insert(bucket); + } + // Test: IP addresses in the different /16 prefix MAY NOT map to more than + // 8 buckets. + ASSERT_TRUE(buckets.size() == 8); + } + + TEST(TestAddrmanTests, caddrinfo_get_new_bucket) + { + CAddrManTest addrman; + + CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); + CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE); + + CNetAddr source1 = ResolveIP("250.1.2.1"); + + CAddrInfo info1 = CAddrInfo(addr1, source1); + + uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash(); + uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash(); + + std::vector asmap = FromBytes(asmap_raw, sizeof(asmap_raw) * 8); + + // Test: Make sure the buckets are what we expect + ASSERT_EQ(info1.GetNewBucket(nKey1, asmap), 795); + ASSERT_EQ(info1.GetNewBucket(nKey1, source1, asmap), 795); + + // Test: Make sure key actually randomizes bucket placement. A fail on + // this test could be a security issue. + ASSERT_TRUE(info1.GetNewBucket(nKey1, asmap) != info1.GetNewBucket(nKey2, asmap)); + + // Test: Ports should not affect bucket placement in the addr + CAddrInfo info2 = CAddrInfo(addr2, source1); + ASSERT_TRUE(info1.GetKey() != info2.GetKey()); + ASSERT_EQ(info1.GetNewBucket(nKey1, asmap), info2.GetNewBucket(nKey1, asmap)); + + std::set buckets; + for (int i = 0; i < 255; i++) { + CAddrInfo infoi = CAddrInfo( + CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE), + ResolveIP("250.1.1." + boost::to_string(i))); + int bucket = infoi.GetNewBucket(nKey1, asmap); + buckets.insert(bucket); + } + // Test: IP addresses in the same /16 prefix + // usually map to the same bucket. + ASSERT_EQ(buckets.size(), 1U); + + buckets.clear(); + for (int j = 0; j < 4 * 255; j++) { + CAddrInfo infoj = CAddrInfo(CAddress( + ResolveService( + boost::to_string(250 + (j / 255)) + "." + boost::to_string(j % 256) + ".1.1"), NODE_NONE), + ResolveIP("251.4.1.1")); + int bucket = infoj.GetNewBucket(nKey1, asmap); + buckets.insert(bucket); + } + // Test: IP addresses in the same source /16 prefix should not map to more + // than 64 buckets. + ASSERT_TRUE(buckets.size() <= 64); + + buckets.clear(); + for (int p = 0; p < 255; p++) { + CAddrInfo infoj = CAddrInfo( + CAddress(ResolveService("250.1.1.1"), NODE_NONE), + ResolveIP("101." + boost::to_string(p) + ".1.1")); + int bucket = infoj.GetNewBucket(nKey1, asmap); + buckets.insert(bucket); + } + // Test: IP addresses in the different source /16 prefixes usually map to MORE + // than 1 bucket. + ASSERT_TRUE(buckets.size() > 1); + + buckets.clear(); + for (int p = 0; p < 255; p++) { + CAddrInfo infoj = CAddrInfo( + CAddress(ResolveService("250.1.1.1"), NODE_NONE), + ResolveIP("250." + boost::to_string(p) + ".1.1")); + int bucket = infoj.GetNewBucket(nKey1, asmap); + buckets.insert(bucket); + } + // Test: IP addresses in the different source /16 prefixes sometimes map to NO MORE + // than 1 bucket. + ASSERT_TRUE(buckets.size() == 1); + } + + TEST(TestAddrmanTests, addrman_serialization) + { + std::vector asmap1 = FromBytes(asmap_raw, sizeof(asmap_raw) * 8); + + CAddrManTest addrman_asmap1(true, asmap1); + CAddrManTest addrman_asmap1_dup(true, asmap1); + CAddrManTest addrman_noasmap; + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + + CAddress addr = CAddress(ResolveService("250.1.1.1"), NODE_NONE); + CNetAddr default_source; + + addrman_asmap1.Add(addr, default_source); + + stream << addrman_asmap1; + // serizalizing/deserializing addrman with the same asmap + stream >> addrman_asmap1_dup; + + std::pair bucketAndEntry_asmap1 = addrman_asmap1.GetBucketAndEntry(addr); + std::pair bucketAndEntry_asmap1_dup = addrman_asmap1_dup.GetBucketAndEntry(addr); + ASSERT_TRUE(bucketAndEntry_asmap1.second != -1); + ASSERT_TRUE(bucketAndEntry_asmap1_dup.second != -1); + + ASSERT_TRUE(bucketAndEntry_asmap1.first == bucketAndEntry_asmap1_dup.first); + ASSERT_TRUE(bucketAndEntry_asmap1.second == bucketAndEntry_asmap1_dup.second); + + // deserializing asmaped peers.dat to non-asmaped addrman + stream << addrman_asmap1; + stream >> addrman_noasmap; + std::pair bucketAndEntry_noasmap = addrman_noasmap.GetBucketAndEntry(addr); + ASSERT_TRUE(bucketAndEntry_noasmap.second != -1); + ASSERT_TRUE(bucketAndEntry_asmap1.first != bucketAndEntry_noasmap.first); + ASSERT_TRUE(bucketAndEntry_asmap1.second != bucketAndEntry_noasmap.second); + + // deserializing non-asmaped peers.dat to asmaped addrman + addrman_asmap1.Clear(); + addrman_noasmap.Clear(); + addrman_noasmap.Add(addr, default_source); + // GTEST_COUT_COLOR << addr.ToString() << " - " << default_source.ToString() << " - " << addrman_noasmap.size() << std::endl; + // addrman_noasmap.PrintInternals(); + stream << addrman_noasmap; + // std::string strHex = HexStr(stream.begin(), stream.end()); + // GTEST_COUT_COLOR << strHex << std::endl; + + stream >> addrman_asmap1; + std::pair bucketAndEntry_asmap1_deser = addrman_asmap1.GetBucketAndEntry(addr); + ASSERT_TRUE(bucketAndEntry_asmap1_deser.second != -1); + ASSERT_TRUE(bucketAndEntry_asmap1_deser.first != bucketAndEntry_noasmap.first); + ASSERT_TRUE(bucketAndEntry_asmap1_deser.first == bucketAndEntry_asmap1_dup.first); + ASSERT_TRUE(bucketAndEntry_asmap1_deser.second == bucketAndEntry_asmap1_dup.second); + + // used to map to different buckets, now maps to the same bucket. + addrman_asmap1.Clear(); + addrman_noasmap.Clear(); + CAddress addr1 = CAddress(ResolveService("250.1.1.1"), NODE_NONE); + CAddress addr2 = CAddress(ResolveService("250.2.1.1"), NODE_NONE); + addrman_noasmap.Add(addr, default_source); + addrman_noasmap.Add(addr2, default_source); + std::pair bucketAndEntry_noasmap_addr1 = addrman_noasmap.GetBucketAndEntry(addr1); + std::pair bucketAndEntry_noasmap_addr2 = addrman_noasmap.GetBucketAndEntry(addr2); + ASSERT_TRUE(bucketAndEntry_noasmap_addr1.first != bucketAndEntry_noasmap_addr2.first); + ASSERT_TRUE(bucketAndEntry_noasmap_addr1.second != bucketAndEntry_noasmap_addr2.second); + stream << addrman_noasmap; + stream >> addrman_asmap1; + std::pair bucketAndEntry_asmap1_deser_addr1 = addrman_asmap1.GetBucketAndEntry(addr1); + std::pair bucketAndEntry_asmap1_deser_addr2 = addrman_asmap1.GetBucketAndEntry(addr2); + ASSERT_TRUE(bucketAndEntry_asmap1_deser_addr1.first == bucketAndEntry_asmap1_deser_addr2.first); + ASSERT_TRUE(bucketAndEntry_asmap1_deser_addr1.second != bucketAndEntry_asmap1_deser_addr2.second); + } + +} diff --git a/src/test-komodo/test_netbase_tests.cpp b/src/test-komodo/test_netbase_tests.cpp new file mode 100644 index 00000000000..563b3a4952b --- /dev/null +++ b/src/test-komodo/test_netbase_tests.cpp @@ -0,0 +1,78 @@ +#include + +#include +#include + +#include "addrman.h" +#include +#include "netbase.h" + +#define GTEST_COUT_NOCOLOR std::cerr << "[ ] [ INFO ] " +namespace testing +{ + namespace internal + { + enum GTestColor { + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW + }; + + extern void ColoredPrintf(GTestColor color, const char* fmt, ...); + } +} +#define PRINTF(...) do { testing::internal::ColoredPrintf(testing::internal::COLOR_GREEN, "[ ] "); testing::internal::ColoredPrintf(testing::internal::COLOR_YELLOW, __VA_ARGS__); } while(0) + +// C++ stream interface +class TestCout : public std::stringstream +{ + public: + ~TestCout() + { + PRINTF("%s",str().c_str()); + } +}; + +#define GTEST_COUT_COLOR TestCout() + +using namespace std; + +static CNetAddr ResolveIP(const std::string& ip) +{ + vector vIPs; + CNetAddr addr; + if (LookupHost(ip.c_str(), vIPs)) { + addr = vIPs[0]; + } else + { + // it was BOOST_CHECK_MESSAGE, but we can't use ASSERT outside a test + GTEST_COUT_COLOR << strprintf("failed to resolve: %s", ip) << std::endl; + } + return addr; +} + +namespace TestNetBaseTests { + + TEST(TestAddrmanTests, netbase_getgroup) { + + std::vector asmap; // use /16 + ASSERT_TRUE(ResolveIP("127.0.0.1").GetGroup(asmap) == std::vector({0})); // Local -> !Routable() + ASSERT_TRUE(ResolveIP("257.0.0.1").GetGroup(asmap) == std::vector({0})); // !Valid -> !Routable() + ASSERT_TRUE(ResolveIP("10.0.0.1").GetGroup(asmap) == std::vector({0})); // RFC1918 -> !Routable() + ASSERT_TRUE(ResolveIP("169.254.1.1").GetGroup(asmap) == std::vector({0})); // RFC3927 -> !Routable() + ASSERT_TRUE(ResolveIP("1.2.3.4").GetGroup(asmap) == std::vector({(unsigned char)NET_IPV4, 1, 2})); // IPv4 + + // std::vector vch = ResolveIP("4.3.2.1").GetGroup(asmap); + // GTEST_COUT_COLOR << boost::to_string((int)vch[0]) << boost::to_string((int)vch[1]) << boost::to_string((int)vch[2]) << std::endl; + + ASSERT_TRUE(ResolveIP("::FFFF:0:102:304").GetGroup(asmap) == std::vector({(unsigned char)NET_IPV4, 1, 2})); // RFC6145 + ASSERT_TRUE(ResolveIP("64:FF9B::102:304").GetGroup(asmap) == std::vector({(unsigned char)NET_IPV4, 1, 2})); // RFC6052 + ASSERT_TRUE(ResolveIP("2002:102:304:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector({(unsigned char)NET_IPV4, 1, 2})); // RFC3964 + ASSERT_TRUE(ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup(asmap) == std::vector({(unsigned char)NET_IPV4, 1, 2})); // RFC4380 + ASSERT_TRUE(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup(asmap) == std::vector({(unsigned char)NET_ONION, 239})); // Tor + ASSERT_TRUE(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net + ASSERT_TRUE(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6 + + } +} diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index fc0600dfc6d..61280244519 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -345,15 +345,15 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) BOOST_CHECK(vAddr1.size() == 0); CAddress addr1 = CAddress(CService("250.250.2.1", 8333)); - addr1.nTime = GetAdjustedTime(); // Set time so isTerrible = false + addr1.nTime = GetTime(); // Set time so isTerrible = false CAddress addr2 = CAddress(CService("250.251.2.2", 9999)); - addr2.nTime = GetAdjustedTime(); + addr2.nTime = GetTime(); CAddress addr3 = CAddress(CService("251.252.2.3", 8333)); - addr3.nTime = GetAdjustedTime(); + addr3.nTime = GetTime(); CAddress addr4 = CAddress(CService("252.253.3.4", 8333)); - addr4.nTime = GetAdjustedTime(); + addr4.nTime = GetTime(); CAddress addr5 = CAddress(CService("252.254.4.5", 8333)); - addr5.nTime = GetAdjustedTime(); + addr5.nTime = GetTime(); CNetAddr source1 = CNetAddr("250.1.2.1"); CNetAddr source2 = CNetAddr("250.2.3.3"); @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) CAddress addr = CAddress(CService(strAddr)); // Ensure that for all addrs in addrman, isTerrible == false. - addr.nTime = GetAdjustedTime(); + addr.nTime = GetTime(); addrman.Add(addr, CNetAddr(strAddr)); if (i % 8 == 0) addrman.Good(addr); diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 40b32a7b378..b508edcf1e5 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -20,7 +20,7 @@ BOOST_AUTO_TEST_CASE(netbase_networks) BOOST_CHECK(CNetAddr("::1").GetNetwork() == NET_UNROUTABLE); BOOST_CHECK(CNetAddr("8.8.8.8").GetNetwork() == NET_IPV4); BOOST_CHECK(CNetAddr("2001::8888").GetNetwork() == NET_IPV6); - BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR); + BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_ONION); } BOOST_AUTO_TEST_CASE(netbase_properties) @@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE(netbase_getgroup) BOOST_CHECK(CNetAddr("64:FF9B::102:304").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC6052 BOOST_CHECK(CNetAddr("2002:102:304:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC3964 BOOST_CHECK(CNetAddr("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC4380 - BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup() == boost::assign::list_of((unsigned char)NET_TOR)(239)); // Tor + BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup() == boost::assign::list_of((unsigned char)NET_ONION)(239)); // Tor BOOST_CHECK(CNetAddr("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV6)(32)(1)(4)(112)(175)); //he.net BOOST_CHECK(CNetAddr("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV6)(32)(1)(32)(1)); //IPv6 } diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp deleted file mode 100644 index 887cfb47613..00000000000 --- a/src/test/timedata_tests.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2011-2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -// -#include "timedata.h" -#include "test/test_bitcoin.h" - -#include - -using namespace std; - -BOOST_FIXTURE_TEST_SUITE(timedata_tests, BasicTestingSetup) - -BOOST_AUTO_TEST_CASE(util_MedianFilter) -{ - CMedianFilter filter(5, 15); - - BOOST_CHECK_EQUAL(filter.median(), 15); - - filter.input(20); // [15 20] - BOOST_CHECK_EQUAL(filter.median(), 17); - - filter.input(30); // [15 20 30] - BOOST_CHECK_EQUAL(filter.median(), 20); - - filter.input(3); // [3 15 20 30] - BOOST_CHECK_EQUAL(filter.median(), 17); - - filter.input(7); // [3 7 15 20 30] - BOOST_CHECK_EQUAL(filter.median(), 15); - - filter.input(18); // [3 7 18 20 30] - BOOST_CHECK_EQUAL(filter.median(), 18); - - filter.input(0); // [0 3 7 18 30] - BOOST_CHECK_EQUAL(filter.median(), 7); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/src/timedata.cpp b/src/timedata.cpp index 9a7a6f6fa6d..35af529cb5b 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2014 The Bitcoin Core developers +// Copyright (c) 2020 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -25,107 +26,56 @@ #include "util.h" #include "utilstrencodings.h" -#include - -using namespace std; - -static CCriticalSection cs_nTimeOffset; -static int64_t nTimeOffset = 0; +CTimeWarning timeWarning; /** - * "Never go to sea with two chronometers; take one or three." - * Our three time sources are: - * - System clock - * - Median of other nodes clocks - * - The user (asking the user to fix the system clock if the first two disagree) + * Warn if we have seen TIMEDATA_WARNING_SAMPLES peer times, in the version messages of the + * first TIMEDATA_MAX_SAMPLES unique (by IP address) peers that connect, that are more than + * TIMEDATA_WARNING_THRESHOLD seconds but less than TIMEDATA_IGNORE_THRESHOLD seconds away + * from local time. */ -int64_t GetTimeOffset() -{ - LOCK(cs_nTimeOffset); - return nTimeOffset; -} -int64_t GetAdjustedTime() +int64_t CTimeWarning::AddTimeData(const CNetAddr& ip, int64_t nTime, int64_t now) { - return GetTime() + GetTimeOffset(); -} - -static int64_t abs64(int64_t n) -{ - return (n >= 0 ? n : -n); -} - -#define BITCOIN_TIMEDATA_MAX_SAMPLES 200 + assert(now >= 0 && now <= INT64_MAX - TIMEDATA_IGNORE_THRESHOLD); -void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) -{ - LOCK(cs_nTimeOffset); - // Ignore duplicates - static set setKnown; - if (setKnown.size() == BITCOIN_TIMEDATA_MAX_SAMPLES) - return; - if (!setKnown.insert(ip).second) - return; + if (nTime <= now - TIMEDATA_IGNORE_THRESHOLD || nTime >= now + TIMEDATA_IGNORE_THRESHOLD) { + return 0; + } - // Add data - static CMedianFilter vTimeOffsets(BITCOIN_TIMEDATA_MAX_SAMPLES, 0); - vTimeOffsets.input(nOffsetSample); - LogPrintf("Added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); + int64_t nTimeOffset = nTime - now; - // There is a known issue here (see issue #4521): - // - // - The structure vTimeOffsets contains up to 200 elements, after which - // any new element added to it will not increase its size, replacing the - // oldest element. - // - // - The condition to update nTimeOffset includes checking whether the - // number of elements in vTimeOffsets is odd, which will never happen after - // there are 200 elements. - // - // But in this case the 'bug' is protective against some attacks, and may - // actually explain why we've never seen attacks which manipulate the - // clock offset. - // - // So we should hold off on fixing this and clean it up as part of - // a timing cleanup that strengthens it in a number of other ways. - // - if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) - { - int64_t nMedian = vTimeOffsets.median(); - std::vector vSorted = vTimeOffsets.sorted(); - // Only let other nodes change our time by so much - if (abs64(nMedian) < 30) // thanks to zawy for pointing this out!! zcash issues 4021 //70 * 60) - { - nTimeOffset = nMedian; - } - else - { - nTimeOffset = 0; + LOCK(cs); + // Ignore duplicate IPs. + if (setKnown.size() == TIMEDATA_MAX_SAMPLES || !setKnown.insert(ip).second) { + return nTimeOffset; + } - static bool fDone; - if (!fDone) - { - // If nobody has a time different than ours but within 5 minutes of ours, give a warning - bool fMatch = false; - BOOST_FOREACH(int64_t nOffset, vSorted) - if (nOffset != 0 && abs64(nOffset) < 5 * 60) - fMatch = true; + LogPrintf("Added time data, samples %d, offset %+d (%+d minutes)\n", setKnown.size(), nTimeOffset, nTimeOffset/60); - if (!fMatch) - { - fDone = true; - string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Zcash will not work properly."); - strMiscWarning = strMessage; - LogPrintf("*** %s\n", strMessage); - uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); - } - } + if (nPeersBehind + nPeersAhead < TIMEDATA_WARNING_SAMPLES) { + if (nTimeOffset < -TIMEDATA_WARNING_THRESHOLD) { + nPeersBehind++; + } else if (nTimeOffset > TIMEDATA_WARNING_THRESHOLD) { + nPeersAhead++; } - if (fDebug) { - BOOST_FOREACH(int64_t n, vSorted) - LogPrintf("%+d ", n); - LogPrintf("| "); + if (nPeersBehind + nPeersAhead == TIMEDATA_WARNING_SAMPLES) { + Warn(nPeersAhead, nPeersBehind); } - LogPrintf("nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset/60); } + return nTimeOffset; +} + +void CTimeWarning::Warn(size_t peersAhead, size_t peersBehind) +{ + std::string strMessage; + if (peersBehind >= TIMEDATA_WARNING_MAJORITY) { + strMessage = _("Warning: Your computer's date and time may be ahead of the rest of the network! If your clock is wrong Zcash will not work properly."); + } else if (peersAhead >= TIMEDATA_WARNING_MAJORITY) { + strMessage = _("Warning: Your computer's date and time may be behind the rest of the network! If your clock is wrong Zcash will not work properly."); + } else { + strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Zcash will not work properly."); + } + LogPrintf("*** %s\n", strMessage); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); } diff --git a/src/timedata.h b/src/timedata.h index 2296baf11bb..6a149d8c592 100644 --- a/src/timedata.h +++ b/src/timedata.h @@ -1,76 +1,38 @@ // Copyright (c) 2014 The Bitcoin Core developers +// Copyright (c) 2020 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_TIMEDATA_H #define BITCOIN_TIMEDATA_H -#include -#include +#include #include -#include +#include "netbase.h" +#include "sync.h" -class CNetAddr; - -/** - * Median filter over a stream of values. - * Returns the median of the last N numbers - */ -template -class CMedianFilter +class CTimeWarning { private: - std::vector vValues; - std::vector vSorted; - unsigned int nSize; + CCriticalSection cs; + std::set setKnown; + size_t nPeersAhead; + size_t nPeersBehind; public: - CMedianFilter(unsigned int size, T initial_value) : nSize(size) - { - vValues.reserve(size); - vValues.push_back(initial_value); - vSorted = vValues; - } - - void input(T value) - { - if (vValues.size() == nSize) { - vValues.erase(vValues.begin()); - } - vValues.push_back(value); - - vSorted.resize(vValues.size()); - std::copy(vValues.begin(), vValues.end(), vSorted.begin()); - std::sort(vSorted.begin(), vSorted.end()); - } - - T median() const - { - int size = vSorted.size(); - assert(size > 0); - if (size & 1) // Odd number of elements - { - return vSorted[size / 2]; - } else // Even number of elements - { - return (vSorted[size / 2 - 1] + vSorted[size / 2]) / 2; - } - } + static const size_t TIMEDATA_WARNING_SAMPLES = 8; + static const size_t TIMEDATA_WARNING_MAJORITY = 6; + static const size_t TIMEDATA_MAX_SAMPLES = 20; + static const int64_t TIMEDATA_WARNING_THRESHOLD = 10 * 60; + static const int64_t TIMEDATA_IGNORE_THRESHOLD = 10 * 24 * 60 * 60; - int size() const - { - return vValues.size(); - } + CTimeWarning() : nPeersBehind(0), nPeersAhead(0) {} + virtual ~CTimeWarning() {} - std::vector sorted() const - { - return vSorted; - } + int64_t AddTimeData(const CNetAddr& ip, int64_t nTime, int64_t now); + virtual void Warn(size_t peersAhead, size_t peersBehind); }; -/** Functions to keep track of adjusted P2P time */ -int64_t GetTimeOffset(); -int64_t GetAdjustedTime(); -void AddTimeData(const CNetAddr& ip, int64_t nTime); +extern CTimeWarning timeWarning; #endif // BITCOIN_TIMEDATA_H diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 99c76995b15..a5bd149b2e5 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -529,8 +529,8 @@ void TorController::auth_cb(TorControlConnection& conn, const TorControlReply& r // if -onion isn't set to something else. if (GetArg("-onion", "") == "") { proxyType addrOnion = proxyType(CService("127.0.0.1", 9050), true); - SetProxy(NET_TOR, addrOnion); - SetLimited(NET_TOR, false); + SetProxy(NET_ONION, addrOnion); + SetLimited(NET_ONION, false); } // Finally - now create the service diff --git a/src/util.cpp b/src/util.cpp index 15eea4e7dcb..fa62ba1ff85 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -712,16 +712,12 @@ boost::filesystem::path GetConfigFile() } void ReadConfigFile(map& mapSettingsRet, - map >& mapMultiSettingsRet,int32_t abortflag) + map >& mapMultiSettingsRet) { boost::filesystem::ifstream streamConfig(GetConfigFile()); if (!streamConfig.good()) - { - if ( abortflag != 0 ) - throw missing_zcash_conf(); - else return; - } + throw missing_zcash_conf(); set setOptions; setOptions.insert("*"); @@ -739,6 +735,8 @@ void ReadConfigFile(map& mapSettingsRet, } // If datadir is changed in .conf file: ClearDatadirCache(); + extern uint16_t BITCOIND_RPCPORT; + BITCOIND_RPCPORT = GetArg("-rpcport",BaseParams().RPCPort()); } #ifndef _WIN32 diff --git a/src/util.h b/src/util.h index 5d7dc882fe8..17bf19952b0 100644 --- a/src/util.h +++ b/src/util.h @@ -151,7 +151,7 @@ class missing_zcash_conf : public std::runtime_error { public: missing_zcash_conf() : std::runtime_error("Missing komodo.conf") { } }; -void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet,int32_t abortflag); +void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); #ifdef _WIN32 boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif diff --git a/src/util/asmap.cpp b/src/util/asmap.cpp new file mode 100644 index 00000000000..bd77d742185 --- /dev/null +++ b/src/util/asmap.cpp @@ -0,0 +1,187 @@ +// Copyright (c) 2019-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +namespace { + +constexpr uint32_t INVALID = 0xFFFFFFFF; + +uint32_t DecodeBits(std::vector::const_iterator& bitpos, const std::vector::const_iterator& endpos, uint8_t minval, const std::vector &bit_sizes) +{ + uint32_t val = minval; + bool bit; + for (std::vector::const_iterator bit_sizes_it = bit_sizes.begin(); + bit_sizes_it != bit_sizes.end(); ++bit_sizes_it) { + if (bit_sizes_it + 1 != bit_sizes.end()) { + if (bitpos == endpos) break; + bit = *bitpos; + bitpos++; + } else { + bit = 0; + } + if (bit) { + val += (1 << *bit_sizes_it); + } else { + for (int b = 0; b < *bit_sizes_it; b++) { + if (bitpos == endpos) return INVALID; // Reached EOF in mantissa + bit = *bitpos; + bitpos++; + val += bit << (*bit_sizes_it - 1 - b); + } + return val; + } + } + return INVALID; // Reached EOF in exponent +} + +enum class Instruction : uint32_t +{ + RETURN = 0, + JUMP = 1, + MATCH = 2, + DEFAULT = 3, +}; + +const std::vector TYPE_BIT_SIZES{0, 0, 1}; +Instruction DecodeType(std::vector::const_iterator& bitpos, const std::vector::const_iterator& endpos) +{ + return Instruction(DecodeBits(bitpos, endpos, 0, TYPE_BIT_SIZES)); +} + +const std::vector ASN_BIT_SIZES{15, 16, 17, 18, 19, 20, 21, 22, 23, 24}; +uint32_t DecodeASN(std::vector::const_iterator& bitpos, const std::vector::const_iterator& endpos) +{ + return DecodeBits(bitpos, endpos, 1, ASN_BIT_SIZES); +} + + +const std::vector MATCH_BIT_SIZES{1, 2, 3, 4, 5, 6, 7, 8}; +uint32_t DecodeMatch(std::vector::const_iterator& bitpos, const std::vector::const_iterator& endpos) +{ + return DecodeBits(bitpos, endpos, 2, MATCH_BIT_SIZES); +} + + +const std::vector JUMP_BIT_SIZES{5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}; +uint32_t DecodeJump(std::vector::const_iterator& bitpos, const std::vector::const_iterator& endpos) +{ + return DecodeBits(bitpos, endpos, 17, JUMP_BIT_SIZES); +} + +} + +uint32_t Interpret(const std::vector &asmap, const std::vector &ip) +{ + std::vector::const_iterator pos = asmap.begin(); + const std::vector::const_iterator endpos = asmap.end(); + uint8_t bits = ip.size(); + uint32_t default_asn = 0; + uint32_t jump, match, matchlen; + Instruction opcode; + while (pos != endpos) { + opcode = DecodeType(pos, endpos); + if (opcode == Instruction::RETURN) { + default_asn = DecodeASN(pos, endpos); + if (default_asn == INVALID) break; // ASN straddles EOF + return default_asn; + } else if (opcode == Instruction::JUMP) { + jump = DecodeJump(pos, endpos); + if (jump == INVALID) break; // Jump offset straddles EOF + if (bits == 0) break; // No input bits left + if (pos + jump < pos) break; // overflow + if (pos + jump >= endpos) break; // Jumping past EOF + if (ip[ip.size() - bits]) { + pos += jump; + } + bits--; + } else if (opcode == Instruction::MATCH) { + match = DecodeMatch(pos, endpos); + if (match == INVALID) break; // Match bits straddle EOF + matchlen = CountBits(match) - 1; + if (bits < matchlen) break; // Not enough input bits + for (uint32_t bit = 0; bit < matchlen; bit++) { + if ((ip[ip.size() - bits]) != ((match >> (matchlen - 1 - bit)) & 1)) { + return default_asn; + } + bits--; + } + } else if (opcode == Instruction::DEFAULT) { + default_asn = DecodeASN(pos, endpos); + if (default_asn == INVALID) break; // ASN straddles EOF + } else { + break; // Instruction straddles EOF + } + } + assert(false); // Reached EOF without RETURN, or aborted (see any of the breaks above) - should have been caught by SanityCheckASMap below + return 0; // 0 is not a valid ASN +} + +bool SanityCheckASMap(const std::vector& asmap, int bits) +{ + const std::vector::const_iterator begin = asmap.begin(), endpos = asmap.end(); + std::vector::const_iterator pos = begin; + std::vector> jumps; // All future positions we may jump to (bit offset in asmap -> bits to consume left) + jumps.reserve(bits); + Instruction prevopcode = Instruction::JUMP; + bool had_incomplete_match = false; + while (pos != endpos) { + uint32_t offset = pos - begin; + if (!jumps.empty() && offset >= jumps.back().first) return false; // There was a jump into the middle of the previous instruction + Instruction opcode = DecodeType(pos, endpos); + if (opcode == Instruction::RETURN) { + if (prevopcode == Instruction::DEFAULT) return false; // There should not be any RETURN immediately after a DEFAULT (could be combined into just RETURN) + uint32_t asn = DecodeASN(pos, endpos); + if (asn == INVALID) return false; // ASN straddles EOF + if (jumps.empty()) { + // Nothing to execute anymore + if (endpos - pos > 7) return false; // Excessive padding + while (pos != endpos) { + if (*pos) return false; // Nonzero padding bit + ++pos; + } + return true; // Sanely reached EOF + } else { + // Continue by pretending we jumped to the next instruction + offset = pos - begin; + if (offset != jumps.back().first) return false; // Unreachable code + bits = jumps.back().second; // Restore the number of bits we would have had left after this jump + jumps.pop_back(); + prevopcode = Instruction::JUMP; + } + } else if (opcode == Instruction::JUMP) { + uint32_t jump = DecodeJump(pos, endpos); + if (jump == INVALID) return false; // Jump offset straddles EOF + if (pos + jump < pos) return false; // overflow + if (pos + jump > endpos) return false; // Jump out of range + if (bits == 0) return false; // Consuming bits past the end of the input + --bits; + uint32_t jump_offset = pos - begin + jump; + if (!jumps.empty() && jump_offset >= jumps.back().first) return false; // Intersecting jumps + jumps.emplace_back(jump_offset, bits); + prevopcode = Instruction::JUMP; + } else if (opcode == Instruction::MATCH) { + uint32_t match = DecodeMatch(pos, endpos); + if (match == INVALID) return false; // Match bits straddle EOF + int matchlen = CountBits(match) - 1; + if (prevopcode != Instruction::MATCH) had_incomplete_match = false; + if (matchlen < 8 && had_incomplete_match) return false; // Within a sequence of matches only at most one should be incomplete + had_incomplete_match = (matchlen < 8); + if (bits < matchlen) return false; // Consuming bits past the end of the input + bits -= matchlen; + prevopcode = Instruction::MATCH; + } else if (opcode == Instruction::DEFAULT) { + if (prevopcode == Instruction::DEFAULT) return false; // There should not be two successive DEFAULTs (they could be combined into one) + uint32_t asn = DecodeASN(pos, endpos); + if (asn == INVALID) return false; // ASN straddles EOF + prevopcode = Instruction::DEFAULT; + } else { + return false; // Instruction straddles EOF + } + } + return false; // Reached EOF without RETURN instruction +} diff --git a/src/util/asmap.h b/src/util/asmap.h new file mode 100644 index 00000000000..b31e639bb5d --- /dev/null +++ b/src/util/asmap.h @@ -0,0 +1,15 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_ASMAP_H +#define BITCOIN_UTIL_ASMAP_H + +#include +#include + +uint32_t Interpret(const std::vector &asmap, const std::vector &ip); + +bool SanityCheckASMap(const std::vector& asmap, int bits); + +#endif // BITCOIN_UTIL_ASMAP_H diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 6e7e3ae4b6a..4c8a9541ebe 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1007,7 +1007,7 @@ UniValue z_exportviewingkey(const UniValue& params, bool fHelp, const CPubKey& m return EncodeViewingKey(vk); } -extern int32_t KOMODO_NSPV,KOMODO_DEX_P2P; +extern int32_t KOMODO_NSPV; #ifndef KOMODO_NSPV_FULLNODE #define KOMODO_NSPV_FULLNODE (KOMODO_NSPV <= 0) #endif // !KOMODO_NSPV_FULLNODE @@ -1029,271 +1029,8 @@ UniValue NSPV_hdrsproof(int32_t prevheight,int32_t nextheight); UniValue NSPV_txproof(int32_t vout,uint256 txid,int32_t height); UniValue NSPV_ccmoduleutxos(char *coinaddr, int64_t amount, uint8_t evalcode, std::string funcids, uint256 filtertxid); -UniValue komodo_DEXbroadcast(uint64_t *locatorp,uint8_t funcid,char *hexstr,int32_t priority,char *tagA,char *tagB,char *destpub33,char *volA,char *volB); -UniValue komodo_DEXlist(uint32_t stopat,int32_t minpriority,char *tagA,char *tagB,char *destpub33,char *minA,char *maxA,char *minB,char *maxB,char *stophashstr); -UniValue komodo_DEXorderbook(int32_t revflag,int32_t maxentries,int32_t minpriority,char *tagA,char *tagB,char *destpub33,char *minA,char *maxA,char *minB,char *maxB); -UniValue komodo_DEXget(uint32_t shorthash); -UniValue komodo_DEXpublish(char *fname,int32_t priority,int32_t sliceid); -UniValue komodo_DEXsubscribe(int32_t &cmpflag,char *fname,int32_t priority,uint32_t shorthash,char *publisher,int32_t sliceid); -UniValue komodo_DEXstream(char *fname,int32_t priority); -UniValue komodo_DEXstreamsub(char *fname,int32_t priority,char *pubkeystr); -UniValue komodo_DEXcancel(char *pubkeystr,uint32_t shorthash,char *tagA,char *tagB); -UniValue komodo_DEXanonsend(char *message,int32_t priority,char *destpub33); -UniValue komodo_DEX_notarize(char *coin,int32_t height); -int32_t is_hexstr(char *str,int32_t n); -int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); -void komodo_DEX_pubkeyupdate(); - -UniValue komodo_DEX_stats(void); uint256 Parseuint256(const char *hexstr); -extern std::string NSPV_address,NOTARY_PUBKEY; -extern uint8_t NOTARY_PUBKEY33[33]; - -UniValue DEX_broadcast(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); int32_t priority = 0; char *hexstr,*tagA=(char *)"",*tagB=(char *)"",*destpub33=(char *)"",*volA=(char *)"",*volB=(char *)""; - if ( fHelp || params.size() == 0 || params.size() > 7 ) - throw runtime_error("DEX_broadcast hex [priority [tagA [tagB [pubkey33 [volA [volB]]]]]]\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_broadcast\n"); - if ( params.size() > 6 ) - volB = (char *)params[6].get_str().c_str(); - if ( params.size() > 5 ) - volA = (char *)params[5].get_str().c_str(); - if ( params.size() > 4 ) - destpub33 = (char *)params[4].get_str().c_str(); - if ( params.size() > 3 ) - tagB = (char *)params[3].get_str().c_str(); - if ( params.size() > 2 ) - tagA = (char *)params[2].get_str().c_str(); - if ( params.size() > 1 ) - priority = atol((char *)params[1].get_str().c_str()); - hexstr = (char *)params[0].get_str().c_str(); - if ( 0 && strcmp(hexstr,"ffff") == 0 ) - { - if ( tagA[0] == 0 ) - tagA = (char *)"base"; - if ( tagB[0] == 0 ) - tagB = (char *)"rel"; - } - result = komodo_DEXbroadcast(0,'Q',hexstr,priority,tagA,tagB,destpub33,volA,volB); - if ( strcmp(hexstr,"ffff") == 0 ) - { - UniValue silentresult; - return(silentresult); - } - return(result); -} - -UniValue DEX_notarize(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - int32_t height; char *coin; - if ( fHelp || params.size() == 0 || params.size() > 2 ) - throw runtime_error("DEX_notarize coin height\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_notarize\n"); - if ( strncmp(ASSETCHAINS_SYMBOL,"DPOW",4) != 0 ) - throw runtime_error("only DPOW chains have DEX_notarize\n"); - if ( params.size() > 1 ) - height = atol((char *)params[1].get_str().c_str()); - coin = (char *)params[0].get_str().c_str(); - return(komodo_DEX_notarize(coin,height)); -} - -UniValue DEX_anonsend(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); int32_t priority = 0; char *message,*destpub33=(char *)""; - if ( fHelp || params.size() == 0 || params.size() > 3 ) - throw runtime_error("DEX_anonsend message priority destpub33\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_anonsend\n"); - if ( params.size() > 2 ) - destpub33 = (char *)params[2].get_str().c_str(); - if ( params.size() > 1 ) - priority = atol((char *)params[1].get_str().c_str()); - message = (char *)params[0].get_str().c_str(); - return(komodo_DEXanonsend(message,priority,destpub33)); -} - -UniValue DEX_list(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - uint32_t stopat = 0; int32_t minpriority = 0; char *tagA=(char *)"",*tagB=(char *)"",*destpub33=(char *)"",*minA=(char *)"",*minB=(char *)"",*maxA=(char *)"",*maxB=(char *)"",*stophashstr=(char *)""; - if ( fHelp || params.size() == 0 || params.size() > 10 ) - throw runtime_error("DEX_list stopat minpriority tagA tagB pubkey33 [minA maxA minB maxB [stophash]]\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_list\n"); - if ( params.size() > 9 ) - stophashstr = (char *)params[9].get_str().c_str(); - if ( params.size() > 8 ) - maxB = (char *)params[8].get_str().c_str(); - if ( params.size() > 7 ) - minB = (char *)params[7].get_str().c_str(); - if ( params.size() > 6 ) - maxA = (char *)params[6].get_str().c_str(); - if ( params.size() > 5 ) - minA = (char *)params[5].get_str().c_str(); - if ( params.size() > 4 ) - destpub33 = (char *)params[4].get_str().c_str(); - if ( params.size() > 3 ) - tagB = (char *)params[3].get_str().c_str(); - if ( params.size() > 2 ) - tagA = (char *)params[2].get_str().c_str(); - if ( params.size() > 1 ) - minpriority = atol((char *)params[1].get_str().c_str()); - if ( params.size() > 0 ) - stopat = atol((char *)params[0].get_str().c_str()); - return(komodo_DEXlist(stopat,minpriority,tagA,tagB,destpub33,minA,maxA,minB,maxB,stophashstr)); -} - -UniValue DEX_orderbook(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); int32_t maxentries=10,minpriority=0; char *tagA=(char *)"",*tagB=(char *)"",*destpub33=(char *)"",*minA=(char *)"",*minB=(char *)"",*maxA=(char *)"",*maxB=(char *)""; - if ( fHelp || params.size() == 0 || params.size() > 9 ) - throw runtime_error("DEX_orderbook maxentries minpriority tagA tagB pubkey33 [minA maxA minB maxB]\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_orderbook\n"); - if ( params.size() > 8 ) - maxB = (char *)params[8].get_str().c_str(); - if ( params.size() > 7 ) - minB = (char *)params[7].get_str().c_str(); - if ( params.size() > 6 ) - maxA = (char *)params[6].get_str().c_str(); - if ( params.size() > 5 ) - minA = (char *)params[5].get_str().c_str(); - if ( params.size() > 4 ) - destpub33 = (char *)params[4].get_str().c_str(); - if ( params.size() > 3 ) - tagB = (char *)params[3].get_str().c_str(); - if ( params.size() > 2 ) - tagA = (char *)params[2].get_str().c_str(); - if ( params.size() > 1 ) - minpriority = atol((char *)params[1].get_str().c_str()); - if ( params.size() > 0 ) - maxentries = atol((char *)params[0].get_str().c_str()); - result.push_back(Pair((char *)"asks",komodo_DEXorderbook(0,maxentries,minpriority,tagA,tagB,destpub33,minA,maxA,minB,maxB))); - result.push_back(Pair((char *)"bids",komodo_DEXorderbook(1,maxentries,minpriority,tagB,tagA,destpub33,minB,maxB,minA,maxA))); - result.push_back(Pair((char *)"base",tagA)); - result.push_back(Pair((char *)"rel",tagB)); - return(result); -} - -UniValue DEX_cancel(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - uint32_t shorthash=0; char *tagA=(char *)"",*tagB=(char *)"",*pubkeystr=(char *)""; - if ( fHelp || params.size() == 0 || params.size() > 4 ) - throw runtime_error("DEX_cancel id [pubkey33 [tagA tagB]]\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_cancel\n"); - if ( params.size() > 3 ) - tagB = (char *)params[3].get_str().c_str(); - if ( params.size() > 2 ) - tagA = (char *)params[2].get_str().c_str(); - if ( params.size() > 1 ) - pubkeystr = (char *)params[1].get_str().c_str(); - if ( params.size() > 0 ) - shorthash = atol((char *)params[0].get_str().c_str()); - return(komodo_DEXcancel(pubkeystr,shorthash,tagA,tagB)); -} - -UniValue DEX_get(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - uint32_t id=0; - if ( fHelp || params.size() == 0 || params.size() > 1 ) - throw runtime_error("DEX_get id\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_get\n"); - if ( params.size() > 0 ) - id = atol((char *)params[0].get_str().c_str()); - return(komodo_DEXget(id)); -} - -UniValue DEX_publish(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - char *fname=(char *)"testfile"; int32_t sliceid=0,priority=0; - if ( fHelp || params.size() > 3 ) - throw runtime_error("DEX_publish filename priority sliceid\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_publish\n"); - if ( params.size() > 2 ) - sliceid = atol((char *)params[2].get_str().c_str()); - if ( params.size() > 1 ) - priority = atol((char *)params[1].get_str().c_str()); - if ( params.size() > 0 ) - fname = (char *)params[0].get_str().c_str(); - return(komodo_DEXpublish(fname,priority,sliceid)); -} - -UniValue DEX_subscribe(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - char *fname=(char *)"testfile",*publisher=(char *)""; uint32_t id; int32_t cmpflag,priority=0; - if ( fHelp || params.size() < 3 || params.size() > 4 ) - throw runtime_error("DEX_subscribe filename priority id [publisher33]\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_subscribe\n"); - if ( params.size() > 3 ) - publisher = (char *)params[3].get_str().c_str(); - if ( params.size() > 2 ) - id = atol((char *)params[2].get_str().c_str()); - if ( params.size() > 1 ) - priority = atol((char *)params[1].get_str().c_str()); - if ( params.size() > 0 ) - fname = (char *)params[0].get_str().c_str(); - return(komodo_DEXsubscribe(cmpflag,fname,priority,id,publisher,0)); -} - -UniValue DEX_stream(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - char *fname=(char *)"testfile"; int32_t priority=0; - if ( fHelp || params.size() > 2 ) - throw runtime_error("DEX_stream filename priority\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_stream\n"); - if ( params.size() > 1 ) - priority = atol((char *)params[1].get_str().c_str()); - if ( params.size() > 0 ) - fname = (char *)params[0].get_str().c_str(); - return(komodo_DEXstream(fname,priority)); -} - -UniValue DEX_streamsub(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - char *fname=(char *)"testfile",*publisher=(char *)""; int32_t priority=0; - if ( fHelp || params.size() > 3 ) - throw runtime_error("DEX_streamsub filename priority pubkey\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_streamsub\n"); - if ( params.size() > 2 ) - publisher = (char *)params[2].get_str().c_str(); - if ( params.size() > 1 ) - priority = atol((char *)params[1].get_str().c_str()); - if ( params.size() > 0 ) - fname = (char *)params[0].get_str().c_str(); - return(komodo_DEXstreamsub(fname,priority,publisher)); -} - -UniValue DEX_stats(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if ( fHelp || params.size() != 0 ) - throw runtime_error("DEX_stats\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_stats\n"); - return(komodo_DEX_stats()); -} - -UniValue DEX_setpubkey(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue p; int32_t n; - if ( fHelp || params.size() != 1 ) - throw runtime_error("DEX_setpubkey pubkey33\n"); - if ( KOMODO_DEX_P2P == 0 ) - throw runtime_error("only -dexp2p nodes have DEX_setpubkey\n"); - if ( is_hexstr((char *)params[0].get_str().c_str(),0) != 66 ) - throw runtime_error("must be 33 bytes of hex\n"); - decode_hex(NOTARY_PUBKEY33,33,(char *)params[0].get_str().c_str()); - NOTARY_PUBKEY = params[0].get_str(); - komodo_DEX_pubkeyupdate(); - return(DEX_stats(p,false,mypk)); -} +extern std::string NSPV_address; UniValue nspv_getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) { @@ -1303,7 +1040,7 @@ UniValue nspv_getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) if ( KOMODO_NSPV_FULLNODE ) throw runtime_error("-nSPV=1 must be set to use nspv\n"); if ( params.size() == 1 ) - reqht = atol((char *)params[0].get_str().c_str()); + reqht = atoi((char *)params[0].get_str().c_str()); return(NSPV_getinfo_req(reqht)); } @@ -1341,9 +1078,9 @@ UniValue nspv_listunspent(const UniValue& params, bool fHelp, const CPubKey& myp if ( params.size() >= 1 ) { if ( params.size() >= 2 ) - CCflag = atol((char *)params[1].get_str().c_str()); + CCflag = atoi((char *)params[1].get_str().c_str()); if ( params.size() == 3 ) - skipcount = atol((char *)params[2].get_str().c_str()); + skipcount = atoi((char *)params[2].get_str().c_str()); return(NSPV_addressutxos((char *)params[0].get_str().c_str(),CCflag,skipcount,0)); } else throw runtime_error("nspv_listunspent [address [isCC [skipcount]]]\n"); @@ -1357,15 +1094,15 @@ UniValue nspv_mempool(const UniValue& params, bool fHelp, const CPubKey& mypk) throw runtime_error("nspv_mempool func(0 all, 1 address recv, 2 txid/vout spent, 3 txid inmempool) address isCC [txid vout]]]\n"); if ( KOMODO_NSPV_FULLNODE ) throw runtime_error("-nSPV=1 must be set to use nspv\n"); - funcid = atol((char *)params[0].get_str().c_str()); + funcid = atoi((char *)params[0].get_str().c_str()); coinaddr = (char *)params[1].get_str().c_str(); - CCflag = atol((char *)params[2].get_str().c_str()); + CCflag = atoi((char *)params[2].get_str().c_str()); if ( params.size() > 3 ) { if ( params.size() != 5 ) throw runtime_error("nspv_mempool func(0 all, 1 address recv, 2 txid/vout spent, 3 txid inmempool) address isCC [txid vout]]]\n"); txid = Parseuint256((char *)params[3].get_str().c_str()); - vout = atol((char *)params[4].get_str().c_str()); + vout = atoi((char *)params[4].get_str().c_str()); } return(NSPV_mempooltxids(coinaddr,CCflag,funcid,txid,vout)); } @@ -1386,9 +1123,9 @@ UniValue nspv_listtransactions(const UniValue& params, bool fHelp, const CPubKey if ( params.size() >= 1 ) { if ( params.size() >= 2 ) - CCflag = atol((char *)params[1].get_str().c_str()); + CCflag = atoi((char *)params[1].get_str().c_str()); if ( params.size() == 3 ) - skipcount = atol((char *)params[2].get_str().c_str()); + skipcount = atoi((char *)params[2].get_str().c_str()); //fprintf(stderr,"call txids cc.%d skip.%d\n",CCflag,skipcount); return(NSPV_addresstxids((char *)params[0].get_str().c_str(),CCflag,skipcount,0)); } @@ -1403,7 +1140,7 @@ UniValue nspv_spentinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) if ( KOMODO_NSPV_FULLNODE ) throw runtime_error("-nSPV=1 must be set to use nspv\n"); txid = Parseuint256((char *)params[0].get_str().c_str()); - vout = atol((char *)params[1].get_str().c_str()); + vout = atoi((char *)params[1].get_str().c_str()); return(NSPV_spentinfo(txid,vout)); } @@ -1414,7 +1151,7 @@ UniValue nspv_notarizations(const UniValue& params, bool fHelp, const CPubKey& m throw runtime_error("nspv_notarizations height\n"); if ( KOMODO_NSPV_FULLNODE ) throw runtime_error("-nSPV=1 must be set to use nspv\n"); - height = atol((char *)params[0].get_str().c_str()); + height = atoi((char *)params[0].get_str().c_str()); return(NSPV_notarizations(height)); } @@ -1425,8 +1162,8 @@ UniValue nspv_hdrsproof(const UniValue& params, bool fHelp, const CPubKey& mypk) throw runtime_error("nspv_hdrsproof prevheight nextheight\n"); if ( KOMODO_NSPV_FULLNODE ) throw runtime_error("-nSPV=1 must be set to use nspv\n"); - prevheight = atol((char *)params[0].get_str().c_str()); - nextheight = atol((char *)params[1].get_str().c_str()); + prevheight = atoi((char *)params[0].get_str().c_str()); + nextheight = atoi((char *)params[1].get_str().c_str()); return(NSPV_hdrsproof(prevheight,nextheight)); } @@ -1438,7 +1175,7 @@ UniValue nspv_txproof(const UniValue& params, bool fHelp, const CPubKey& mypk) if ( KOMODO_NSPV_FULLNODE ) throw runtime_error("-nSPV=1 must be set to use nspv\n"); txid = Parseuint256((char *)params[0].get_str().c_str()); - height = atol((char *)params[1].get_str().c_str()); + height = atoi((char *)params[1].get_str().c_str()); return(NSPV_txproof(0,txid,height)); } @@ -1481,8 +1218,8 @@ UniValue nspv_listccmoduleunspent(const UniValue& params, bool fHelp, const CPub std::string address = params[0].get_str().c_str(); int64_t amount = atof(params[1].get_str().c_str()); - uint8_t evalcode = atol(params[2].get_str().c_str()); + uint8_t evalcode = atoi(params[2].get_str().c_str()); std::string funcids = params[3].get_str().c_str(); uint256 txid = Parseuint256( params[4].get_str().c_str() ); return(NSPV_ccmoduleutxos((char*)address.c_str(), amount, evalcode, funcids, txid)); -} +} \ No newline at end of file diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 3806dc9aed8..1231953b18f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -501,15 +501,13 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); } -int32_t komodo_opreturnscript(uint8_t *script,uint8_t type,uint8_t *opret,int32_t opretlen); UniValue sendtoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) { - uint8_t opretbuf[IGUANA_MAXSCRIPTSIZE],opretscript[IGUANA_MAXSCRIPTSIZE],*opret=0; char *oprethexstr; int32_t len,opretlen = 0; if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - if (fHelp || params.size() < 2 || params.size() > 6) + if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( "sendtoaddress \"" + strprintf("%s",komodo_chainname()) + "_address\" amount ( \"comment\" \"comment-to\" subtractfeefromamount )\n" "\nSend an amount to a given address. The amount is a real and is rounded to the nearest 0.00000001\n" @@ -523,7 +521,7 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) " to which you're sending the transaction. This is not part of the \n" " transaction, just kept in your wallet.\n" "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" - " The recipient will receive less " + strprintf("%s",komodo_chainname()) + " than you enter in the amount field.\n6. oprethexstr" + " The recipient will receive less " + strprintf("%s",komodo_chainname()) + " than you enter in the amount field.\n" "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" "\nExamples:\n" @@ -562,20 +560,10 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) bool fSubtractFeeFromAmount = false; if (params.size() > 4) fSubtractFeeFromAmount = params[4].get_bool(); - if (params.size() > 5) - { - oprethexstr = (char *)params[5].get_str().c_str(); - if ( (len= is_hexstr(oprethexstr,0)) > 1 && len <= sizeof(opretbuf)*2 ) - { - len >>= 1; - decode_hex(opretbuf,len,oprethexstr); - opretlen = komodo_opreturnscript(opretscript,0x00,opretbuf,len); - opret = opretscript; - } else opretlen = 0; - } + EnsureWalletIsUnlocked(); - SendMoney(dest, nAmount, fSubtractFeeFromAmount, wtx,opret,opretlen,0); + SendMoney(dest, nAmount, fSubtractFeeFromAmount, wtx,0,0,0); return wtx.GetHash().GetHex(); } @@ -1352,7 +1340,7 @@ UniValue movecmd(const UniValue& params, bool fHelp, const CPubKey& mypk) if (!walletdb.TxnBegin()) throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); - int64_t nNow = GetAdjustedTime(); + int64_t nNow = GetTime(); // Debit CAccountingEntry debit; @@ -5092,7 +5080,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp LOCK2(cs_main, pwalletMain->cs_wallet); - THROW_IF_SYNCING(KOMODO_INSYNC); + //THROW_IF_SYNCING(KOMODO_INSYNC); bool useAnyUTXO = false; bool useAnySprout = false; @@ -5597,6 +5585,7 @@ int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits #include "../cc/CCfaucet.h" +#include "../cc/CCassets.h" #include "../cc/CCrewards.h" #include "../cc/CCdice.h" #include "../cc/CCfsm.h" @@ -5880,7 +5869,7 @@ UniValue cclibinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) UniValue cclib(const UniValue& params, bool fHelp, const CPubKey& mypk) { - struct CCcontract_info *cp,C; char *method, *jsonstr=0; uint8_t evalcode = EVAL_FIRSTUSER; + struct CCcontract_info *cp,C; char *method,*jsonstr=0; uint8_t evalcode = EVAL_FIRSTUSER; std::string vobjJsonSerialized; if ( fHelp || params.size() > 3 ) @@ -6082,6 +6071,19 @@ UniValue pegsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) return(CCaddress(cp,(char *)"Pegs",pubkey)); } +UniValue marmaraaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + struct CCcontract_info *cp,C; std::vector pubkey; + cp = CCinit(&C,EVAL_MARMARA); + if ( fHelp || params.size() > 1 ) + throw runtime_error("Marmaraaddress [pubkey]\n"); + if ( ensure_CCrequirements(cp->evalcode) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + if ( params.size() == 1 ) + pubkey = ParseHex(params[0].get_str().c_str()); + return(CCaddress(cp,(char *)"Marmara",pubkey)); +} + UniValue paymentsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) { struct CCcontract_info *cp,C; std::vector pubkey; @@ -6200,12 +6202,38 @@ UniValue rewardsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) return(CCaddress(cp,(char *)"Rewards",pubkey)); } +UniValue assetsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + struct CCcontract_info *cp, C; std::vector pubkey; + cp = CCinit(&C, EVAL_ASSETS); + if (fHelp || params.size() > 1) + throw runtime_error("assetsaddress [pubkey]\n"); + if (ensure_CCrequirements(cp->evalcode) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + if (params.size() == 1) + pubkey = ParseHex(params[0].get_str().c_str()); + return(CCaddress(cp, (char *)"Assets", pubkey)); +} + +UniValue tokenaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + struct CCcontract_info *cp,C; std::vector pubkey; + cp = CCinit(&C,EVAL_TOKENS); + if ( fHelp || params.size() > 1 ) + throw runtime_error("tokenaddress [pubkey]\n"); + if ( ensure_CCrequirements(cp->evalcode) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + if ( params.size() == 1 ) + pubkey = ParseHex(params[0].get_str().c_str()); + return(CCaddress(cp,(char *)"Tokens", pubkey)); +} + UniValue importgatewayaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) { struct CCcontract_info *cp,C; std::vector pubkey; cp = CCinit(&C,EVAL_IMPORTGATEWAY); if ( fHelp || params.size() > 1 ) - throw runtime_error("importgatewayaddress [pubkey]\n"); + throw runtime_error("importgatewayddress [pubkey]\n"); if ( ensure_CCrequirements(cp->evalcode) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); if ( params.size() == 1 ) @@ -6213,6 +6241,192 @@ UniValue importgatewayaddress(const UniValue& params, bool fHelp, const CPubKey& return(CCaddress(cp,(char *)"ImportGateway", pubkey)); } +UniValue marmara_poolpayout(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + int32_t firstheight; double perc; char *jsonstr; + if ( fHelp || params.size() != 3 ) + { + // marmarapoolpayout 0.5 2 '[["024131032ed90941e714db8e6dd176fe5a86c9d873d279edecf005c06f773da686",1000],["02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92",100]]'; + //marmarapoolpayout 0 2 '[["024131032ed90941e714db8e6dd176fe5a86c9d873d279edecf005c06f773da686",1000]]' + throw runtime_error("marmarapoolpayout perc firstheight \"[[\\\"pubkey\\\":shares], ...]\"\n"); + } + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + perc = atof(params[0].get_str().c_str()) / 100.; + firstheight = atol(params[1].get_str().c_str()); + jsonstr = (char *)params[2].get_str().c_str(); + return(MarmaraPoolPayout(0,firstheight,perc,jsonstr)); // [[pk0, shares0], [pk1, shares1], ...] +} + +UniValue marmara_receive(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); uint256 batontxid; std::vector senderpub; int64_t amount; int32_t matures; std::string currency; + if ( fHelp || (params.size() != 5 && params.size() != 4) ) + { + // automatic flag -> lsb of matures + // 1st marmarareceive 028076d42eb20efc10007fafb5ca66a2052523c0d2221e607adf958d1a332159f6 7.5 MARMARA 1440 + // after marmarareceive 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 7.5 MARMARA 1168 d72d87aa0d50436de695c93e2bf3d7273c63c92ef6307913aa01a6ee6a16548b + throw runtime_error("marmarareceive senderpk amount currency matures batontxid\n"); + } + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + memset(&batontxid,0,sizeof(batontxid)); + senderpub = ParseHex(params[0].get_str().c_str()); + if (senderpub.size()!= 33) + { + ERR_RESULT("invalid sender pubkey"); + return result; + } + amount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; + currency = params[2].get_str(); + if ( params.size() == 5 ) + { + matures = atol(params[3].get_str().c_str()); + batontxid = Parseuint256((char *)params[4].get_str().c_str()); + } else matures = atol(params[3].get_str().c_str()) + chainActive.LastTip()->GetHeight() + 1; + return(MarmaraReceive(0,pubkey2pk(senderpub),amount,currency,matures,batontxid,true)); +} + +UniValue marmara_issue(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); uint256 approvaltxid; std::vector receiverpub; int64_t amount; int32_t matures; std::string currency; + if ( fHelp || params.size() != 5 ) + { + // marmaraissue 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 7.5 MARMARA 1168 32da4cb3e886ee42de90b4a15042d71169077306badf909099c5c5c692df3f27 + // marmaraissue 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 700 MARMARA 2629 11fe8bf1de80c2ef69124d08907f259aef7f41e3a632ca2d48ad072a8c8f3078 -> 335df3a5dd6b92a3d020c9465d4d76e0d8242126106b83756dcecbad9813fdf3 + + throw runtime_error("marmaraissue receiverpk amount currency matures approvaltxid\n"); + } + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + receiverpub = ParseHex(params[0].get_str().c_str()); + if (receiverpub.size()!= 33) + { + ERR_RESULT("invalid receiverpub pubkey"); + return result; + } + amount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; + currency = params[2].get_str(); + matures = atol(params[3].get_str().c_str()); + approvaltxid = Parseuint256((char *)params[4].get_str().c_str()); + return(MarmaraIssue(0,'I',pubkey2pk(receiverpub),amount,currency,matures,approvaltxid,zeroid)); +} + +UniValue marmara_transfer(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); uint256 approvaltxid,batontxid; std::vector receiverpub; int64_t amount; int32_t matures; std::string currency; std::vector creditloop; + if ( fHelp || params.size() != 5 ) + { + // marmaratransfer 028076d42eb20efc10007fafb5ca66a2052523c0d2221e607adf958d1a332159f6 7.5 MARMARA 1168 1506c774e4b2804a6e25260920840f4cfca8d1fb400e69fe6b74b8e593dbedc5 + throw runtime_error("marmaratransfer receiverpk amount currency matures approvaltxid\n"); + } + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + receiverpub = ParseHex(params[0].get_str().c_str()); + if (receiverpub.size()!= 33) + { + ERR_RESULT("invalid receiverpub pubkey"); + return result; + } + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + amount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; + currency = params[2].get_str(); + matures = atol(params[3].get_str().c_str()); + approvaltxid = Parseuint256((char *)params[4].get_str().c_str()); + if ( MarmaraGetbatontxid(creditloop,batontxid,approvaltxid) < 0 ) + throw runtime_error("couldnt find batontxid\n"); + return(MarmaraIssue(0,'T',pubkey2pk(receiverpub),amount,currency,matures,approvaltxid,batontxid)); +} + +UniValue marmara_info(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); CPubKey issuerpk; std::vector issuerpub; int64_t minamount,maxamount; int32_t firstheight,lastheight; std::string currency; + if ( fHelp || params.size() < 4 || params.size() > 6 ) + { + throw runtime_error("marmarainfo firstheight lastheight minamount maxamount [currency issuerpk]\n"); + } + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + firstheight = atol(params[0].get_str().c_str()); + lastheight = atol(params[1].get_str().c_str()); + minamount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; + maxamount = atof(params[3].get_str().c_str()) * COIN + 0.00000000499999; + if ( params.size() >= 5 ) + currency = params[4].get_str(); + if ( params.size() == 6 ) + { + issuerpub = ParseHex(params[5].get_str().c_str()); + if ( issuerpub.size()!= 33 ) + { + ERR_RESULT("invalid issuer pubkey"); + return result; + } + issuerpk = pubkey2pk(issuerpub); + } + result = MarmaraInfo(issuerpk,firstheight,lastheight,minamount,maxamount,currency); + return(result); +} + +UniValue marmara_creditloop(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); uint256 txid; + if ( fHelp || params.size() != 1 ) + { + // marmaracreditloop 010ff7f9256cefe3b5dee3d72c0eeae9fc6f34884e6f32ffe5b60916df54a9be + throw runtime_error("marmaracreditloop txid\n"); + } + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + txid = Parseuint256((char *)params[0].get_str().c_str()); + result = MarmaraCreditloop(txid); + return(result); +} + +UniValue marmara_settlement(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); uint256 batontxid; + if ( fHelp || params.size() != 1 ) + { + // marmarasettlement 010ff7f9256cefe3b5dee3d72c0eeae9fc6f34884e6f32ffe5b60916df54a9be + // marmarasettlement ff3e259869196f3da9b5ea3f9e088a76c4fc063cf36ab586b652e121d441a603 + throw runtime_error("marmarasettlement batontxid\n"); + } + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + batontxid = Parseuint256((char *)params[0].get_str().c_str()); + result = MarmaraSettlement(0,batontxid); + return(result); +} + +UniValue marmara_lock(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); int64_t amount; int32_t height; + if ( fHelp || params.size() > 2 || params.size() == 0 ) + { + throw runtime_error("marmaralock amount unlockht\n"); + } + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + amount = atof(params[0].get_str().c_str()) * COIN + 0.00000000499999; + if ( params.size() == 2 ) + height = atol(params[1].get_str().c_str()); + else height = chainActive.LastTip()->GetHeight() + 1; + return(MarmaraLock(0,amount,height)); +} + UniValue channelslist(const UniValue& params, bool fHelp, const CPubKey& mypk) { if ( fHelp || params.size() > 0 ) @@ -6238,28 +6452,22 @@ UniValue channelsinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) UniValue channelsopen(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); int32_t numpayments; int64_t payment; std::vector destpub; struct CCcontract_info *cp,C; - uint256 tokenid=zeroid; uint16_t confirmation=1; + uint256 tokenid=zeroid; cp = CCinit(&C,EVAL_CHANNELS); - if ( fHelp || params.size() < 3 || params.size() > 5) - throw runtime_error("channelsopen destpubkey numpayments payment [confirmation] [tokenid]\n"); + if ( fHelp || params.size() < 3 || params.size() > 4) + throw runtime_error("channelsopen destpubkey numpayments payment [tokenid]\n"); if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); Lock2NSPV(mypk); destpub = ParseHex(params[0].get_str().c_str()); numpayments = atoi(params[1].get_str().c_str()); payment = atol(params[2].get_str().c_str()); - if (params.size()==4) - { - if (params[3].get_str().size()>sizeof(confirmation)) tokenid = Parseuint256((char *)params[3].get_str().c_str()); - else confirmation = atoi(params[3].get_str().c_str()); - } - if (params.size()==5) + if (params.size()==4) { - confirmation = atoi(params[3].get_str().c_str()); - tokenid=Parseuint256((char *)params[4].get_str().c_str()); + tokenid=Parseuint256((char *)params[3].get_str().c_str()); } - result = ChannelOpen(mypk,0,pubkey2pk(destpub),numpayments,payment,confirmation,tokenid); + result = ChannelOpen(mypk,0,pubkey2pk(destpub),numpayments,payment,tokenid); if ( result[JSON_HEXTX].getValStr().size() > 0 ) { result.push_back(Pair("result", "success")); @@ -6292,26 +6500,6 @@ UniValue channelspayment(const UniValue& params, bool fHelp, const CPubKey& mypk return(result); } -UniValue channelsgeneratesecret(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 opentxid; int32_t n; int64_t amount; - cp = CCinit(&C,EVAL_CHANNELS); - if ( fHelp || params.size() < 2 || params.size() >3 ) - throw runtime_error("channelspayment opentxid amount\n"); - if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - Lock2NSPV(mypk); - opentxid = Parseuint256((char *)params[0].get_str().c_str()); - amount = atoi((char *)params[1].get_str().c_str()); - result = ChannelGenerateSecret(mypk,opentxid,amount); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) - { - result.push_back(Pair("result", "success")); - } - Unlock2NSPV(mypk); - return(result); -} - UniValue channelsclose(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 opentxid; @@ -6650,7 +6838,7 @@ UniValue gatewaysdeposit(const UniValue& params, bool fHelp, const CPubKey& mypk { UniValue result(UniValue::VOBJ); int32_t i,claimvout,height; int64_t amount; std::string coin,deposithex; uint256 bindtxid,cointxid; std::vectorproof,destpub,pubkey; if ( fHelp || params.size() != 9 ) - throw runtime_error("gatewaysdeposit bindtxid height coin cointxid markervout deposithex proof destpubkey amount\n"); + throw runtime_error("gatewaysdeposit bindtxid height coin cointxid claimvout deposithex proof destpub amount\n"); if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); Lock2NSPV(mypk); @@ -6662,8 +6850,7 @@ UniValue gatewaysdeposit(const UniValue& params, bool fHelp, const CPubKey& mypk deposithex = params[5].get_str(); proof = ParseHex(params[6].get_str()); destpub = ParseHex(params[7].get_str()); - //amount = atof((char *)params[8].get_str().c_str()) * COIN + 0.00000000499999; - amount = AmountFromValue(params[8]); + amount = atof((char *)params[8].get_str().c_str()) * COIN + 0.00000000499999; if ( amount <= 0 || claimvout < 0 ) { Unlock2NSPV(mypk); @@ -6683,6 +6870,33 @@ UniValue gatewaysdeposit(const UniValue& params, bool fHelp, const CPubKey& mypk return(result); } +UniValue gatewaysclaim(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); std::string coin; uint256 bindtxid,deposittxid; std::vectordestpub; int64_t amount; + if ( fHelp || params.size() != 5 ) + throw runtime_error("gatewaysclaim bindtxid coin deposittxid destpub amount\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + Lock2NSPV(mypk); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + deposittxid = Parseuint256((char *)params[2].get_str().c_str()); + destpub = ParseHex(params[3].get_str()); + amount = atof((char *)params[4].get_str().c_str()) * COIN + 0.00000000499999; + if (destpub.size()!= 33) + { + Unlock2NSPV(mypk); + throw runtime_error("invalid destination pubkey"); + } + result = GatewaysClaim(mypk,0,bindtxid,coin,deposittxid,pubkey2pk(destpub),amount); + if ( result[JSON_HEXTX].getValStr().size() > 0 ) + { + result.push_back(Pair("result", "success")); + } + Unlock2NSPV(mypk); + return(result); +} + UniValue gatewayswithdraw(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); uint256 bindtxid; int64_t amount; std::string coin; std::vector withdrawpub; @@ -6694,8 +6908,7 @@ UniValue gatewayswithdraw(const UniValue& params, bool fHelp, const CPubKey& myp bindtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); withdrawpub = ParseHex(params[2].get_str()); - //amount = atof((char *)params[3].get_str().c_str()) * COIN + 0.00000000499999; - amount = AmountFromValue(params[3]); + amount = atof((char *)params[3].get_str().c_str()) * COIN + 0.00000000499999; if (withdrawpub.size()!= 33) { Unlock2NSPV(mypk); @@ -6706,22 +6919,42 @@ UniValue gatewayswithdraw(const UniValue& params, bool fHelp, const CPubKey& myp { result.push_back(Pair("result", "success")); } + Lock2NSPV(mypk); + return(result); +} + +UniValue gatewayspartialsign(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); std::string coin,parthex; uint256 txid; + if ( fHelp || params.size() != 3 ) + throw runtime_error("gatewayspartialsign txidaddr refcoin hex\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + Lock2NSPV(mypk); + txid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + parthex = params[2].get_str(); + result = GatewaysPartialSign(mypk,0,txid,coin,parthex); + if ( result[JSON_HEXTX].getValStr().size() > 0 ) + { + result.push_back(Pair("result", "success")); + } Unlock2NSPV(mypk); return(result); } -UniValue gatewayswithdrawsign(const UniValue& params, bool fHelp, const CPubKey& mypk) +UniValue gatewayscompletesigning(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); uint256 withdrawtxid; std::string txhex,coin; if ( fHelp || params.size() != 3 ) - throw runtime_error("gatewayswithdrawsign withdrawtxid coin hex\n"); + throw runtime_error("gatewayscompletesigning withdrawtxid coin hex\n"); if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); Lock2NSPV(mypk); withdrawtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); txhex = params[2].get_str(); - result = GatewaysWithdrawSign(mypk,0,withdrawtxid,coin,txhex); + result = GatewaysCompleteSigning(mypk,0,withdrawtxid,coin,txhex); if ( result[JSON_HEXTX].getValStr().size() > 0 ) { result.push_back(Pair("result", "success")); @@ -6748,6 +6981,7 @@ UniValue gatewaysmarkdone(const UniValue& params, bool fHelp, const CPubKey& myp Unlock2NSPV(mypk); return(result); } + UniValue gatewayspendingdeposits(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 bindtxid; std::string coin; @@ -6760,28 +6994,28 @@ UniValue gatewayspendingdeposits(const UniValue& params, bool fHelp, const CPubK return(GatewaysPendingDeposits(mypk,bindtxid,coin)); } -UniValue gatewayspendingsignwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk) +UniValue gatewayspendingwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 bindtxid; std::string coin; if ( fHelp || params.size() != 2 ) - throw runtime_error("gatewayspendingsignwithdraws bindtxid coin\n"); + throw runtime_error("gatewayspendingwithdraws bindtxid coin\n"); if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); - return(GatewaysPendingSignWithdraws(mypk,bindtxid,coin)); + return(GatewaysPendingWithdraws(mypk,bindtxid,coin)); } -UniValue gatewayssignedwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk) +UniValue gatewaysprocessed(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 bindtxid; std::string coin; if ( fHelp || params.size() != 2 ) - throw runtime_error("gatewayssignedwithdraws bindtxid coin\n"); + throw runtime_error("gatewaysprocessed bindtxid coin\n"); if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); - return(GatewaysSignedWithdraws(mypk,bindtxid,coin)); + return(GatewaysProcessedWithdraws(mypk,bindtxid,coin)); } UniValue oracleslist(const UniValue& params, bool fHelp, const CPubKey& mypk) @@ -7315,6 +7549,497 @@ UniValue diceinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) return(DiceInfo(fundingtxid)); } +UniValue tokenlist(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + uint256 tokenid; + if ( fHelp || params.size() > 0 ) + throw runtime_error("tokenlist\n"); + if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + return(TokenList()); +} + +UniValue tokeninfo(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + uint256 tokenid; + if ( fHelp || params.size() != 1 ) + throw runtime_error("tokeninfo tokenid\n"); + if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + tokenid = Parseuint256((char *)params[0].get_str().c_str()); + return(TokenInfo(tokenid)); +} + +UniValue tokenorders(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + uint256 tokenid; + if ( fHelp || params.size() > 1 ) + throw runtime_error("tokenorders [tokenid]\n" + "returns token orders for the tokenid or all available token orders if tokenid is not set\n" + "(this rpc supports only fungible tokens)\n" "\n"); + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + if (params.size() == 1) { + tokenid = Parseuint256((char *)params[0].get_str().c_str()); + if (tokenid == zeroid) + throw runtime_error("incorrect tokenid\n"); + return AssetOrders(tokenid, CPubKey(), 0); + } + else { + // throw runtime_error("no tokenid\n"); + return AssetOrders(zeroid, CPubKey(), 0); + } +} + + +UniValue mytokenorders(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + uint256 tokenid; + if (fHelp || params.size() > 1) + throw runtime_error("mytokenorders [evalcode]\n" + "returns all the token orders for mypubkey\n" + "if evalcode is set then returns mypubkey token orders for non-fungible tokens with this evalcode\n" "\n"); + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + uint8_t additionalEvalCode = 0; + if (params.size() == 1) + additionalEvalCode = strtol(params[0].get_str().c_str(), NULL, 0); // supports also 0xEE-like values + + return AssetOrders(zeroid, Mypubkey(), additionalEvalCode); +} + +UniValue tokenbalance(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); uint256 tokenid; uint64_t balance; std::vector pubkey; struct CCcontract_info *cp,C; + CCerror.clear(); + + if ( fHelp || params.size() > 2 ) + throw runtime_error("tokenbalance tokenid [pubkey]\n"); + if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + + LOCK(cs_main); + + tokenid = Parseuint256((char *)params[0].get_str().c_str()); + if ( params.size() == 2 ) + pubkey = ParseHex(params[1].get_str().c_str()); + else + pubkey = Mypubkey(); + + balance = GetTokenBalance(pubkey2pk(pubkey),tokenid); + + if (CCerror.empty()) { + char destaddr[64]; + + result.push_back(Pair("result", "success")); + cp = CCinit(&C,EVAL_TOKENS); + if (GetCCaddress(cp, destaddr, pubkey2pk(pubkey)) != 0) + result.push_back(Pair("CCaddress", destaddr)); + + result.push_back(Pair("tokenid", params[0].get_str())); + result.push_back(Pair("balance", (int64_t)balance)); + } + else { + ERR_RESULT(CCerror); + } + + return(result); +} + +UniValue tokencreate(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); + std::string name, description, hextx; + std::vector nonfungibleData; + int64_t supply; // changed from uin64_t to int64_t for this 'if ( supply <= 0 )' to work as expected + + CCerror.clear(); + + if ( fHelp || params.size() > 4 || params.size() < 2 ) + throw runtime_error("tokencreate name supply [description][data]\n"); + if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + + name = params[0].get_str(); + if (name.size() == 0 || name.size() > 32) { + ERR_RESULT("Token name must not be empty and up to 32 characters"); + return(result); + } + + supply = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; // what for is this '+0.00000000499999'? it will be lost while converting double to int64_t (dimxy) + if (supply <= 0) { + ERR_RESULT("Token supply must be positive"); + return(result); + } + + if (params.size() >= 3) { + description = params[2].get_str(); + if (description.size() > 4096) { + ERR_RESULT("Token description must be <= 4096 characters"); + return(result); + } + } + + if (params.size() == 4) { + nonfungibleData = ParseHex(params[3].get_str()); + if (nonfungibleData.size() > IGUANA_MAXSCRIPTSIZE) // opret limit + { + ERR_RESULT("Non-fungible data size must be <= " + std::to_string(IGUANA_MAXSCRIPTSIZE)); + return(result); + } + if( nonfungibleData.empty() ) { + ERR_RESULT("Non-fungible data incorrect"); + return(result); + } + } + + hextx = CreateToken(0, supply, name, description, nonfungibleData); + if( hextx.size() > 0 ) { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hextx)); + } + else + ERR_RESULT(CCerror); + return(result); +} + +UniValue tokentransfer(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); + std::string hex; + int64_t amount; + uint256 tokenid; + + CCerror.clear(); + + if ( fHelp || params.size() != 3) + throw runtime_error("tokentransfer tokenid destpubkey amount\n"); + if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + + tokenid = Parseuint256((char *)params[0].get_str().c_str()); + std::vector pubkey(ParseHex(params[1].get_str().c_str())); + //amount = atol(params[2].get_str().c_str()); + amount = atoll(params[2].get_str().c_str()); // dimxy changed to prevent loss of significance + if( tokenid == zeroid ) { + ERR_RESULT("invalid tokenid"); + return(result); + } + if( amount <= 0 ) { + ERR_RESULT("amount must be positive"); + return(result); + } + + hex = TokenTransfer(0, tokenid, pubkey, amount); + + if( !CCerror.empty() ) { + ERR_RESULT(CCerror); + } + else { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } + return(result); +} + +UniValue tokenconvert(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); std::string hex; int32_t evalcode; int64_t amount; uint256 tokenid; + if ( fHelp || params.size() != 4 ) + throw runtime_error("tokenconvert evalcode tokenid pubkey amount\n"); + if ( ensure_CCrequirements(EVAL_ASSETS) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + evalcode = atoi(params[0].get_str().c_str()); + tokenid = Parseuint256((char *)params[1].get_str().c_str()); + std::vector pubkey(ParseHex(params[2].get_str().c_str())); + //amount = atol(params[3].get_str().c_str()); + amount = atoll(params[3].get_str().c_str()); // dimxy changed to prevent loss of significance + if ( tokenid == zeroid ) + { + ERR_RESULT("invalid tokenid"); + return(result); + } + if ( amount <= 0 ) + { + ERR_RESULT("amount must be positive"); + return(result); + } + + ERR_RESULT("deprecated"); + return(result); + +/* hex = AssetConvert(0,tokenid,pubkey,amount,evalcode); + if (amount > 0) { + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt convert tokens"); + } else { + ERR_RESULT("amount must be positive"); + } + return(result); */ +} + +UniValue tokenbid(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); int64_t bidamount,numtokens; std::string hex; double price; uint256 tokenid; + if ( fHelp || params.size() != 3 ) + throw runtime_error("tokenbid numtokens tokenid price\n"); + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + //numtokens = atoi(params[0].get_str().c_str()); + numtokens = atoll(params[0].get_str().c_str()); // dimxy changed to prevent loss of significance + tokenid = Parseuint256((char *)params[1].get_str().c_str()); + price = atof(params[2].get_str().c_str()); + bidamount = (price * numtokens) * COIN + 0.0000000049999; + if ( price <= 0 ) + { + ERR_RESULT("price must be positive"); + return(result); + } + if ( tokenid == zeroid ) + { + ERR_RESULT("invalid tokenid"); + return(result); + } + if ( bidamount <= 0 ) + { + ERR_RESULT("bid amount must be positive"); + return(result); + } + hex = CreateBuyOffer(0,bidamount,tokenid,numtokens); + if (price > 0 && numtokens > 0) { + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt create bid"); + } else { + ERR_RESULT("price and numtokens must be positive"); + } + return(result); +} + +UniValue tokencancelbid(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); std::string hex; int32_t i; uint256 tokenid,bidtxid; + if ( fHelp || params.size() != 2 ) + throw runtime_error("tokencancelbid tokenid bidtxid\n"); + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + tokenid = Parseuint256((char *)params[0].get_str().c_str()); + bidtxid = Parseuint256((char *)params[1].get_str().c_str()); + if ( tokenid == zeroid || bidtxid == zeroid ) + { + result.push_back(Pair("error", "invalid parameter")); + return(result); + } + hex = CancelBuyOffer(0,tokenid,bidtxid); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt cancel bid"); + return(result); +} + +UniValue tokenfillbid(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); int64_t fillamount; std::string hex; uint256 tokenid,bidtxid; + if ( fHelp || params.size() != 3 ) + throw runtime_error("tokenfillbid tokenid bidtxid fillamount\n"); + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + tokenid = Parseuint256((char *)params[0].get_str().c_str()); + bidtxid = Parseuint256((char *)params[1].get_str().c_str()); + // fillamount = atol(params[2].get_str().c_str()); + fillamount = atoll(params[2].get_str().c_str()); // dimxy changed to prevent loss of significance + if ( fillamount <= 0 ) + { + ERR_RESULT("fillamount must be positive"); + return(result); + } + if ( tokenid == zeroid || bidtxid == zeroid ) + { + ERR_RESULT("must provide tokenid and bidtxid"); + return(result); + } + hex = FillBuyOffer(0,tokenid,bidtxid,fillamount); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt fill bid"); + return(result); +} + +UniValue tokenask(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); int64_t askamount,numtokens; std::string hex; double price; uint256 tokenid; + if ( fHelp || params.size() != 3 ) + throw runtime_error("tokenask numtokens tokenid price\n"); + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + //numtokens = atoi(params[0].get_str().c_str()); + numtokens = atoll(params[0].get_str().c_str()); // dimxy changed to prevent loss of significance + tokenid = Parseuint256((char *)params[1].get_str().c_str()); + price = atof(params[2].get_str().c_str()); + askamount = (price * numtokens) * COIN + 0.0000000049999; + //std::cerr << std::boolalpha << "tokenask(): (tokenid == zeroid) is " << (tokenid == zeroid) << " (numtokens <= 0) is " << (numtokens <= 0) << " (price <= 0) is " << (price <= 0) << " (askamount <= 0) is " << (askamount <= 0) << std::endl; + if ( tokenid == zeroid || numtokens <= 0 || price <= 0 || askamount <= 0 ) + { + ERR_RESULT("invalid parameter"); + return(result); + } + hex = CreateSell(0,numtokens,tokenid,askamount); + if (price > 0 && numtokens > 0) { + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt create ask"); + } else { + ERR_RESULT("price and numtokens must be positive"); + } + return(result); +} + +UniValue tokenswapask(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + static uint256 zeroid; + UniValue result(UniValue::VOBJ); int64_t askamount,numtokens; std::string hex; double price; uint256 tokenid,otherid; + if ( fHelp || params.size() != 4 ) + throw runtime_error("tokenswapask numtokens tokenid otherid price\n"); + if ( ensure_CCrequirements(EVAL_ASSETS) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + //numtokens = atoi(params[0].get_str().c_str()); + numtokens = atoll(params[0].get_str().c_str()); // dimxy changed to prevent loss of significance + tokenid = Parseuint256((char *)params[1].get_str().c_str()); + otherid = Parseuint256((char *)params[2].get_str().c_str()); + price = atof(params[3].get_str().c_str()); + askamount = (price * numtokens); + hex = CreateSwap(0,numtokens,tokenid,otherid,askamount); + if (price > 0 && numtokens > 0) { + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt create swap"); + } else { + ERR_RESULT("price and numtokens must be positive"); + } + return(result); +} + +UniValue tokencancelask(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); std::string hex; int32_t i; uint256 tokenid,asktxid; + if ( fHelp || params.size() != 2 ) + throw runtime_error("tokencancelask tokenid asktxid\n"); + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + tokenid = Parseuint256((char *)params[0].get_str().c_str()); + asktxid = Parseuint256((char *)params[1].get_str().c_str()); + if ( tokenid == zeroid || asktxid == zeroid ) + { + result.push_back(Pair("error", "invalid parameter")); + return(result); + } + hex = CancelSell(0,tokenid,asktxid); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt cancel ask"); + return(result); +} + +UniValue tokenfillask(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue result(UniValue::VOBJ); int64_t fillunits; std::string hex; uint256 tokenid,asktxid; + if ( fHelp || params.size() != 3 ) + throw runtime_error("tokenfillask tokenid asktxid fillunits\n"); + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + tokenid = Parseuint256((char *)params[0].get_str().c_str()); + asktxid = Parseuint256((char *)params[1].get_str().c_str()); + //fillunits = atol(params[2].get_str().c_str()); + fillunits = atoll(params[2].get_str().c_str()); // dimxy changed to prevent loss of significance + if ( fillunits <= 0 ) + { + ERR_RESULT("fillunits must be positive"); + return(result); + } + if ( tokenid == zeroid || asktxid == zeroid ) + { + result.push_back(Pair("error", "invalid parameter")); + return(result); + } + hex = FillSell(0,tokenid,zeroid,asktxid,fillunits); + if (fillunits > 0) { + if (CCerror != "") { + ERR_RESULT(CCerror); + } else if ( hex.size() > 0) { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else { + ERR_RESULT("couldnt fill ask"); + } + } else { + ERR_RESULT("fillunits must be positive"); + } + return(result); +} + +UniValue tokenfillswap(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + static uint256 zeroid; + UniValue result(UniValue::VOBJ); int64_t fillunits; std::string hex; uint256 tokenid,otherid,asktxid; + if ( fHelp || params.size() != 4 ) + throw runtime_error("tokenfillswap tokenid otherid asktxid fillunits\n"); + if ( ensure_CCrequirements(EVAL_ASSETS) < 0 ) + throw runtime_error(CC_REQUIREMENTS_MSG); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + tokenid = Parseuint256((char *)params[0].get_str().c_str()); + otherid = Parseuint256((char *)params[1].get_str().c_str()); + asktxid = Parseuint256((char *)params[2].get_str().c_str()); + //fillunits = atol(params[3].get_str().c_str()); + fillunits = atoll(params[3].get_str().c_str()); // dimxy changed to prevent loss of significance + hex = FillSell(0,tokenid,otherid,asktxid,fillunits); + if (fillunits > 0) { + if ( hex.size() > 0 ) { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt fill bid"); + } else { + ERR_RESULT("fillunits must be positive"); + } + return(result); +} UniValue getbalance64(const UniValue& params, bool fHelp, const CPubKey& mypk) { @@ -7577,35 +8302,14 @@ UniValue pegsredeem(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); uint256 pegstxid,tokenid; int64_t amount; - if ( fHelp || params.size()!=3) - throw runtime_error("pegsredeem pegstxid tokenid amount\n"); - if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - Lock2NSPV(mypk); - pegstxid = Parseuint256(params[0].get_str().c_str()); - tokenid = Parseuint256(params[1].get_str().c_str()); - amount = atof((char *)params[2].get_str().c_str()) * COIN + 0.00000000499999; - result = PegsRedeem(mypk,0,pegstxid,tokenid,amount); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) - { - result.push_back(Pair("result", "success")); - } - Unlock2NSPV(mypk); - return(result); -} - -UniValue pegsclose(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); uint256 pegstxid,tokenid; - if ( fHelp || params.size()!=2) - throw runtime_error("pegsclose pegstxid tokenid\n"); + throw runtime_error("pegsredeem pegstxid tokenid\n"); if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); Lock2NSPV(mypk); pegstxid = Parseuint256(params[0].get_str().c_str()); tokenid = Parseuint256(params[1].get_str().c_str()); - result = PegsClose(mypk,0,pegstxid,tokenid); + result = PegsRedeem(mypk,0,pegstxid,tokenid); if ( result[JSON_HEXTX].getValStr().size() > 0 ) { result.push_back(Pair("result", "success")); diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index 3e7a54d4afe..7739e94a2e2 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -23,6 +23,5 @@ class CRPCTable; void RegisterWalletRPCCommands(CRPCTable &tableRPC); -bool EnsureWalletIsAvailable(bool avoidException); #endif //BITCOIN_WALLET_RPCWALLET_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 4c43ed1767b..db66de052c4 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1607,7 +1607,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD bool fInsertedNew = ret.second; if (fInsertedNew) { - wtx.nTimeReceived = GetAdjustedTime(); + wtx.nTimeReceived = GetTime(); wtx.nOrderPos = IncOrderPosNext(pwalletdb); wtx.nTimeSmart = wtx.nTimeReceived; @@ -3876,33 +3876,30 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt BOOST_FOREACH (const CRecipient& recipient, vecSend) { CTxOut txout(recipient.nAmount, recipient.scriptPubKey); - if ( txout.scriptPubKey[0] != OP_RETURN ) + + if (recipient.fSubtractFeeFromAmount) { - if (recipient.fSubtractFeeFromAmount) + txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count { - txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient - - if (fFirst) // first receiver pays the remainder not divisible by output count - { - fFirst = false; - txout.nValue -= nFeeRet % nSubtractFeeFromAmount; - } + fFirst = false; + txout.nValue -= nFeeRet % nSubtractFeeFromAmount; } - //fprintf(stderr,"txout: %02x %02x %02x\n",txout.scriptPubKey[0],txout.scriptPubKey[1],txout.scriptPubKey[2]); - - if ( txout.IsDust(::minRelayTxFee)) + } + + if (txout.IsDust(::minRelayTxFee)) + { + if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) { - if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) - { - if (txout.nValue < 0) - strFailReason = _("The transaction amount is too small to pay the fee"); - else - strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); - } + if (txout.nValue < 0) + strFailReason = _("The transaction amount is too small to pay the fee"); else - strFailReason = _("Transaction amount too small"); - return false; + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); } + else + strFailReason = _("Transaction amount too small"); + return false; } txNew.vout.push_back(txout); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3cb9ba67c85..1b38934f1aa 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1518,6 +1518,6 @@ class AddSpendingKeyToWallet : public boost::static_visitor