diff --git a/.github/workflows/komodo_linux_ci.yml b/.github/workflows/komodo_linux_ci.yml index 948a1b7b07d..65903aca9bb 100644 --- a/.github/workflows/komodo_linux_ci.yml +++ b/.github/workflows/komodo_linux_ci.yml @@ -1,8 +1,8 @@ name: Komodo Linux CI on: - pull_request: - types: [opened, synchronize, reopened] +# pull_request: +# types: [opened, synchronize, reopened] schedule: - cron: '0 0 * * 1' diff --git a/.github/workflows/komodo_mac_ci.yml b/.github/workflows/komodo_mac_ci.yml index 61b4b2f0a33..9bb994963ca 100644 --- a/.github/workflows/komodo_mac_ci.yml +++ b/.github/workflows/komodo_mac_ci.yml @@ -43,7 +43,7 @@ jobs: name: komodo-macos path: ./komodo-macos.tar.gz - macos-test-dice-token-reards-faucet-cc: + macos-test-dice-token-rewards-faucet-cc: name: Test (MacOS/Dice, Token, Faucet, Rewards) runs-on: macos-latest @@ -55,6 +55,7 @@ jobs: - name: Install deps (Dice, Token, Faucet, Rewards CC) run: | brew install python3 curl + python3 -m pip install --no-cache-dir --compile --ignore-installed --install-option="--with-openssl" --install-option="--openssl-dir=/usr/local/opt/openssl@1.1" pycurl python3 -m pip install setuptools wheel python3 -m pip install slick-bitcoinrpc pytest wget jsonschema @@ -68,6 +69,7 @@ jobs: mv komodo-macos/komodo-macos.tar.gz . mkdir -p src tar xzvf komodo-macos.tar.gz + sysctl -n machdep.cpu.brand_string ./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" @@ -84,6 +86,7 @@ jobs: - name: Install deps (OraclesCC) run: | brew install python3 curl + python3 -m pip install --no-cache-dir --compile --ignore-installed --install-option="--with-openssl" --install-option="--openssl-dir=/usr/local/opt/openssl@1.1" pycurl python3 -m pip install setuptools wheel python3 -m pip install slick-bitcoinrpc pytest wget jsonschema - name: Download komodo-macos.tar.gz @@ -112,6 +115,7 @@ jobs: - name: Install deps (BasicRPC) run: | brew install python3 curl + python3 -m pip install --no-cache-dir --compile --ignore-installed --install-option="--with-openssl" --install-option="--openssl-dir=/usr/local/opt/openssl@1.1" pycurl python3 -m pip install setuptools wheel python3 -m pip install slick-bitcoinrpc pytest wget jsonschema - name: Download komodo-macos.tar.gz @@ -140,6 +144,7 @@ jobs: - name: Install deps (ChannelsCC) run: | brew install python3 curl + python3 -m pip install --no-cache-dir --compile --ignore-installed --install-option="--with-openssl" --install-option="--openssl-dir=/usr/local/opt/openssl@1.1" pycurl python3 -m pip install setuptools wheel python3 -m pip install slick-bitcoinrpc pytest wget jsonschema - name: Download komodo-macos.tar.gz @@ -168,6 +173,7 @@ jobs: - name: Install deps (HeirCC) run: | brew install python3 curl + python3 -m pip install --no-cache-dir --compile --ignore-installed --install-option="--with-openssl" --install-option="--openssl-dir=/usr/local/opt/openssl@1.1" pycurl python3 -m pip install setuptools wheel python3 -m pip install slick-bitcoinrpc pytest wget jsonschema - name: Download komodo-macos.tar.gz diff --git a/.github/workflows/komodo_win_ci.yml b/.github/workflows/komodo_win_ci.yml index b74370575bf..5a7d02c7019 100644 --- a/.github/workflows/komodo_win_ci.yml +++ b/.github/workflows/komodo_win_ci.yml @@ -32,7 +32,7 @@ jobs: libncurses-dev \ unzip \ git \ - python \ + python3 \ zlib1g-dev \ wget \ bsdmainutils \ @@ -72,151 +72,117 @@ jobs: name: komodod_win path: ./komodod_win.zip - windows-test-baserpc: - - name: Test (Win/BasicRPC) + windows-build-prereqs: + name: Get Prereqs needs: windows-build runs-on: windows-latest steps: - uses: actions/checkout@v2 + with: + submodules: 'recursive' - name: Download komodo_win.zip uses: actions/download-artifact@v1 with: name: komodod_win - - name: Install deps (Base) + - name: Unpack Komodo 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 + + - name: Install vcpkg + uses: lukka/run-vcpkg@v4 + with: + setupOnly: true + # vcpkgArguments: 'curl' + vcpkgGitCommitId: 'f46f924c08b1fca7ad450ff0b1b26dc9554a3c8e' + # vcpkgDirectory: '${{ github.workspace }}/vcpkg' + # working-directory: ${{env.GITHUB_WORKSPACE}} + + - name: Install libcurl + run: | + vcpkg install curl + shell: cmd + + - name: Install deps (Base) + shell: cmd + run: | + dir /s vcpkg\packages\curl_x64-windows + python3.exe -m pip install --upgrade setuptools + python3.exe -m pip install --upgrade pip + python3.exe -m pip install --no-cache-dir --compile --ignore-installed --install-option="--with-openssl" --curl-dir="vcpkg\packages\curl_x64-windows\lib" pycurl + python3.exe -m pip install pytest wget jsonschema + python3.exe -m pip install slick-bitcoinrpc zcutil\fetch-params.bat + + windows-test-baserpc: + name: Test (Win/BasicRPC) + runs-on: windows-latest + needs: windows-build-prereqs + #with: + # working-directory: ${{env.GITHUB_WORKSPACE}} + + steps: - name: Base RPC Test (Windows) shell: cmd run: | cd qa\pytest_komodo start_ci.bat basic - windows-test-dice-faucet-tok-rewCC: + windows-test-dice-faucet-tok-rewCC: name: Test (Win/Dice Faucet Token Rewards) runs-on: windows-latest - needs: windows-build + needs: windows-build-prereqs + #with: + # working-directory: ${{env.GITHUB_WORKSPACE}} 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: + windows-test-oracles-cc: name: Test (Win/OraclesCC) runs-on: windows-latest - needs: windows-build + needs: windows-build-prereqs + #with: + # working-directory: ${{env.GITHUB_WORKSPACE}} 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: + windows-test-heir-cc: name: Test (Win/HeirCC) runs-on: windows-latest - needs: windows-build + needs: windows-build-prereqs + #with: + # working-directory: ${{env.GITHUB_WORKSPACE}} 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: + windows-test-channels-cc: name: Test (Win/ChannelsCC) runs-on: windows-latest - needs: windows-build + needs: windows-build-prereqs + #with: + # working-directory: ${{env.GITHUB_WORKSPACE}} 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: | diff --git a/Makefile.am b/Makefile.am index 51d0430aca4..298ce8fe824 100644 --- a/Makefile.am +++ b/Makefile.am @@ -344,3 +344,6 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-man clean-local: rm -rf test_bitcoin.coverage/ zcash-gtest.coverage/ total.coverage/ + +clean-all: clean-local + $(MAKE) -C src clean-all \ No newline at end of file diff --git a/README.md b/README.md index c8f99f2e16f..9a00be2da70 100644 --- a/README.md +++ b/README.md @@ -1,203 +1,92 @@ -[![Build Status](https://travis-ci.org/KomodoPlatform/komodo.svg?branch=master)](https://travis-ci.org/KomodoPlatform/komodo) -[![Version](https://img.shields.io/github/v/release/komodoplatform/komodo)](https://github.com/KomodoPlatform/komodo/releases) -[![Issues](https://img.shields.io/github/issues-raw/komodoplatform/komodo)](https://github.com/KomodoPlatform/komodo/issues) -[![PRs](https://img.shields.io/github/issues-pr-closed/komodoplatform/komodo)](https://github.com/KomodoPlatform/komodo/pulls) -[![Commits](https://img.shields.io/github/commit-activity/y/komodoplatform/komodo)](https://github.com/KomodoPlatform/komodo/commits/dev) -[![Contributors](https://img.shields.io/github/contributors/komodoplatform/komodo)](https://github.com/KomodoPlatform/komodo/graphs/contributors) -[![Last Commit](https://img.shields.io/github/last-commit/komodoplatform/komodo)](https://github.com/KomodoPlatform/komodo/graphs/commit-activity) - -[![gitstars](https://img.shields.io/github/stars/komodoplatform/komodo?style=social)](https://github.com/KomodoPlatform/komodo/stargazers) -[![twitter](https://img.shields.io/twitter/follow/komodoplatform?style=social)](https://twitter.com/komodoplatform) -[![discord](https://img.shields.io/discord/412898016371015680)](https://discord.gg/tvp96Gf) - ---- -![Komodo Logo](https://i.imgur.com/E8LtkAa.png "Komodo Logo") - - -## Komodo - -This is the official Komodo sourcecode repository based on https://github.com/jl777/komodo. - -## Development Resources - -- Komodo Website: [https://komodoplatform.com](https://komodoplatform.com/) -- Komodo Blockexplorer: [https://kmdexplorer.io](https://kmdexplorer.io/) -- Komodo Discord: [https://komodoplatform.com/discord](https://komodoplatform.com/discord) -- Forum: [https://forum.komodoplatform.com](https://forum.komodoplatform.com/) -- Mail: [info@komodoplatform.com](mailto:info@komodoplatform.com) -- Support: [https://support.komodoplatform.com/support/home](https://support.komodoplatform.com/support/home) -- Knowledgebase & How-to: [https://support.komodoplatform.com/en/support/solutions](https://support.komodoplatform.com/en/support/solutions) -- API references & Dev Documentation: [https://developers.komodoplatform.com](https://developers.komodoplatform.com/) -- Blog: [https://blog.komodoplatform.com](https://blog.komodoplatform.com/) -- Whitepaper: [Komodo Whitepaper](https://komodoplatform.com/whitepaper) -- Komodo Platform public material: [Komodo Platform public material](https://docs.google.com/document/d/1AbhWrtagu4vYdkl-vsWz-HSNyNvK-W-ZasHCqe7CZy0) - -## List of Komodo Platform Technologies - -- Delayed Proof of Work (dPoW) - Additional security layer and Komodos own consensus algorithm -- zk-SNARKs - Komodo Platform's privacy technology for shielded transactions -- Tokens/Assets Technology - create "colored coins" on the Komodo Platform and use them as a layer for securites -- Reward API - Komodo CC technology for securities -- CC - Crypto Conditions to realize "smart contract" logic on top of the Komodo Platform -- Jumblr - Decentralized tumbler for KMD and other cryptocurrencies -- Assetchains - Create your own Blockchain that inherits all Komodo Platform functionalities and blockchain interoperability -- Pegged Assets - Chains that maintain a peg to fiat currencies -- Peerchains - Scalability solution where sibling chains form a network of blockchains -- More in depth covered [here](https://docs.google.com/document/d/1AbhWrtagu4vYdkl-vsWz-HSNyNvK-W-ZasHCqe7CZy0) -- Also note you receive 5% Active User Reward on your balance. -[See this article for more details](https://support.komodoplatform.com/en/support/solutions/articles/29000024515-how-to-claim-the-kmd-active-user-reward-in-agama) - -## Tech Specification -- Max Supply: 200 million KMD +![Tokel Header](https://github.com/TokelPlatform/tokel_app/blob/development/assets/tokel-header.png "Tokel Header") + + +## The Tokel Blockchain +This repository is required to run and use the main Tokel blockchain and hosts Tokel specific developments. + +To run Tokel's test chain (TKLTEST), please use the 'tkltest' branch instead. +- [https://github.com/TokelPlatform/tokel](https://github.com/TokelPlatform/tokel/tree/tkltest) + + +## What is the Tokel Platform? +Tokel is an open-source, decentralized, token & NFT platform. + +The Tokel platform makes tokenization easy and affordable for creators, businesses and projects by offering inexpensive, user-friendly solutions for token/NFT creation, asset management, and an on-chain decentralized trading functionality without the need for complicated smart contracts or expensive gas fees. + +Utilizing cutting edge UTXO based technology, the Tokel blockchain offers an array of layer 1 blockchain features, removing the need for expensive smart contract development. + +Tokel's super-lite, next-gen SPV technology gives users the ability to interact with their tokens in a decentralized & trust-less fashion on any device, without the inconvenience and energy cost of downloading the entire blockchain. + +Tokel provides universal, accessible, and easy-to-use tokenization technology to people all over the world. + +To NFT and Beyond + +#### What Tokel offers: +- The blockchain that facilitates the tokens and NFTs. +- An open-sourced all-in-one [GUI application](https://github.com/TokelPlatform/tokel_dapp). +- DEX functionality for all tokens/NFTs created on Tokel. +- A free-to-use test blockchain (TKLTEST). +- Easy to use integrations (nSPV) +- Easy to use token/NFT creation, usage and management tools [(within the dApp)](https://github.com/TokelPlatform/tokel_dapp). +- Immediate [token explorer integration](https://explorer.tokel.io/tokens). +- Immediate token wallet integration. + +#### The benefits of creating a token on the Tokel blockchain: +- There are no requirements to create and manage your own blockchain or complicated smart contracts. +- No gas fees! +- Free, simple to use [dApp](https://github.com/TokelPlatform/tokel_dapp) for token usage, management and creation. +- Extremely cheap token creation and transaction fees (1 satoshi of TKL per token created and 0.0001 TKL txfee by default). +- Free to use TKLTEST chain for any token or custom consensus testing prior to launch or integration. +- Your token will have immediate token wallet integration within the dApp. +- Your token will have immediate token explorer integration. +- Your token is safe from 51% attacks as Tokel is secured through [dPoW](https://komodoplatform.com/en/blog/delayed-proof-of-work/). +- You will have the ability to sell your tokens immediately after creation with Tokel’s inbuilt decentralized exchange mechanism. +- Tokel exchange partnerships mean your token will have a centralized exchange listing option. The exchanges are only required to run the Tokel chain to access every single token on it. + +## The Tokel Decentralized Application +The all-in-one Tokel application is an open-sourced application that will be the one stop shop for all Tokel related features. This application is built using nSPV superlite wallet technology that has been developed by the Komodo platform, for an incredibly fast and reliable experience. This application accesses all of the features offered on the Tokel blockchain whilst keeping users funds in their own wallets (completely non-custodial). + +You can keep up with progress on the github, or find out more by having a chat in our discord. +https://github.com/TokelPlatform/tokel_dapp + +#### Application features include: +- A TKL coin wallet +- A multi-token wallet (you are able to hold all tokens that are in existence on the Tokel blockchain) +- Decentralized exchanging functionality +- A simple to use Token creation tool + +## Tokel Resources + +- Tokel Website: [https://tokel.io](https://tokel.io) +- Tokel Block Explorer: [https://explorer.tokel.io](https://explorer.tokel.io) +- Tokel Discord: [Tokel Discord Invitation](http://discord.tokel.io) +- Tokel Blog: [Tokel Blog](https://tokel.io/blog/) +- Email: [contact@tokel.io](mailto:contact@tokel.io) +- Whitepaper: [Tokel Paper](https://tokel.io/TokelPaper1stEdition.pdf) +- Knowledgebase & How-to: [Tokel Documentation](https://docs.tokel.io) + - [How to run the Tokel blockchain](https://docs.tokel.io/guides/LaunchTheChain/) + - [Tokens RPC/API documentation](https://docs.tokel.io/api/tokens/) + - [Assets RPC/API documentation](https://docs.tokel.io/api/assets/) + - [How to run the Tokel test chain](https://docs.tokel.io/guides/LaunchTKLTESTchain/) + +## List of Tokel Platform Technologies + +- [Delayed Proof of Work (dPoW)](https://blog.komodoplatform.com/en/delayed-proof-of-work/)- Bitcoin hashrate level protection. An additional security layer that protects from 51% attacks by notarizing Tokels hash onto Komodo's chain, then on to the Bitcoin blockchain. +- [Tokens](https://docs.tokel.io/api/tokens/)/[Assets](https://docs.tokel.io/api/assets/) Technology - Create your own tokens or non-fungible tokens (NFTs) and trade them immediately on-chain with the assets based tokenDEX. + +## Tokel Blockchain Specifics + +[Documentation on how to launch & run the Tokel blockchain (CLI).](https://docs.tokel.io/guides/LaunchTheChain/) + +- Max Supply: Approximately 200.25 million TKL - Block Time: 60 seconds -- Block Reward: 3 KMD -- Mining Algorithm: Equihash - -## About this Project -Komodo is based on Zcash and has been extended by our innovative consensus algorithm called dPoW which utilizes Bitcoin's hashrate to store Komodo blockchain information into the Bitcoin blockchain. Other new and native Komodo features are the privacy technology called JUMBLR, our assetchain capabilities (one click plug and play blockchain solutions) and a set of financial decentralization and interoperability technologies. More details are available under https://komodoplatform.com/ and https://blog.komodoplatform.com. - -## Getting started - -### Dependencies - -```shell -#The following packages are needed: -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 libsodium-dev -``` - -### Build Komodo - -This software is based on zcash and considered experimental and is continously undergoing heavy development. - -The dev branch is considered the bleeding edge codebase while the master-branch is considered tested (unit tests, runtime tests, functionality). At no point of time do the Komodo Platform developers take any responsbility for any damage out of the usage of this software. -Komodo builds for all operating systems out of the same codebase. Follow the OS specific instructions from below. - -#### Linux -```shell -git clone https://github.com/komodoplatform/komodo --branch master --single-branch -cd komodo -./zcutil/fetch-params.sh -./zcutil/build.sh -j$(expr $(nproc) - 1) -#This can take some time. -``` - - -#### OSX -Ensure you have [brew](https://brew.sh) and Command Line Tools installed. -```shell -# Install brew -/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" -# Install Xcode, opens a pop-up window to install CLT without installing the entire Xcode package -xcode-select --install -# Update brew and install dependencies -brew update -brew upgrade -brew tap discoteq/discoteq; brew install flock -brew install autoconf autogen automake -brew update && brew install gcc@8 -brew install binutils -brew install protobuf -brew install coreutils -brew install wget -# Clone the Komodo repo -git clone https://github.com/komodoplatform/komodo --branch master --single-branch -# Change master branch to other branch you wish to compile -cd komodo -./zcutil/fetch-params.sh -./zcutil/build-mac.sh -j$(expr $(sysctl -n hw.ncpu) - 1) -# This can take some time. -``` - -#### Windows -Use a debian cross-compilation setup with mingw for windows and run: -```shell -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 libsodium-dev libevent-dev -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 -./zcutil/build-win.sh -j$(expr $(nproc) - 1) -#This can take some time. -``` -**komodo is experimental and a work-in-progress.** Use at your own risk. - -To reset the Komodo blockchain change into the *~/.komodo* data directory and delete the corresponding files by running `rm -rf blocks chainstate debug.log komodostate db.log` - -#### Create komodo.conf - -Create a komodo.conf file: - -``` -mkdir ~/.komodo -cd ~/.komodo -touch komodo.conf - -#Add the following lines to the komodo.conf file: -rpcuser=yourrpcusername -rpcpassword=yoursecurerpcpassword -rpcbind=127.0.0.1 -txindex=1 -addnode=77.75.121.138 -addnode=95.213.238.100 -addnode=94.130.148.142 -addnode=103.6.12.105 -addnode=139.99.209.214 -addnode=185.130.212.13 -addnode=5.9.142.219 -addnode=200.25.4.38 -addnode=139.99.136.148 - -``` -### Create your own Blockchain based on Komodo - -Komodo allows anyone to create a runtime fork which represents an independent Blockchain. Below are the detailed instructions: -Setup two independent servers with at least 1 server having a static IP and build komodod on those servers. - -#### On server 1 (with static IP) run: -```shell -./komodod -ac_name=name_of_your_chain -ac_supply=100000 -bind=ip_of_server_1 & -``` - -#### On server 2 run: -```shell -./komodod -ac_name=name_of_your_chain -ac_supply=100000 -addnode=ip_of_server_1 -gen & -``` - -**Komodo is based on Zcash which is unfinished and highly experimental.** Use at your own risk. - -License -------- -For license information see the file [COPYING](COPYING). - -**NOTE TO EXCHANGES:** -https://bitcointalk.org/index.php?topic=1605144.msg17732151#msg17732151 -There is a small chance that an outbound transaction will give an error due to mismatched values in wallet calculations. There is a -exchange option that you can run komodod with, but make sure to have the entire transaction history under the same -exchange mode. Otherwise you will get wallet conflicts. - -**To change modes:** - -a) backup all privkeys (launch komodod with `-exportdir=` and `dumpwallet`) -b) start a totally new sync including `wallet.dat`, launch with same `exportdir` -c) stop it before it gets too far and import all the privkeys from a) using `komodo-cli importwallet filename` -d) resume sync till it gets to chaintip - -For example: -```shell -./komodod -exportdir=/tmp & -./komodo-cli dumpwallet example -./komodo-cli stop -mv ~/.komodo ~/.komodo.old && mkdir ~/.komodo && cp ~/.komodo.old/komodo.conf ~/.komodo.old/peers.dat ~/.komodo -./komodod -exchange -exportdir=/tmp & -./komodo-cli importwallet /tmp/example -``` ---- +- Starting Block Reward (Era 1): 1 TKL (for ~8 weeks) +- Block reward after ~8 weeks: 42.5 TKL +- Block reward reduction time period: Every 525600 blocks following Era 1 (roughly every year) +- Reduction amount: 23% +- Mining Algorithm: Equihash - Proof of Work - Secured by dPoW +- Specific coin emission broken down in the whitepaper + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/configure.ac b/configure.ac index e0629a1c56d..71ae7c0a4ae 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,6 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) +dnl zcash version define(_CLIENT_VERSION_MAJOR, 3) define(_CLIENT_VERSION_MINOR, 0) define(_CLIENT_VERSION_REVISION, 0) @@ -7,13 +8,23 @@ define(_CLIENT_VERSION_BUILD, 0) define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50))) define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1))) define(_CLIENT_VERSION_IS_RELEASE, true) -define(_COPYRIGHT_YEAR, 2018) -AC_INIT([Verus-CLI],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_SUFFIX(_ZC_BUILD_VAL)],[https://github.com/VerusCoin/VerusCoin/issues],[verus-cli]) +define(_COPYRIGHT_YEAR, 2022) +AC_INIT([tokel-CLI],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_SUFFIX(_ZC_BUILD_VAL)],[https://github.com/TokelPlatform/komodo/issues],[tokel-cli]) AC_CONFIG_SRCDIR([src/main.cpp]) AC_CONFIG_HEADERS([src/config/bitcoin-config.h]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([build-aux/m4]) +dnl komodo codebase version +define(_KOMODO_VERSION, 60000) + +dnl tokel codebase version +define(_TOKEL_VERSION, 30200) + +AC_DEFINE(KOMODO_VERSION, _KOMODO_VERSION, [Komodo Version]) +AC_DEFINE(TOKEL_VERSION, _TOKEL_VERSION, [Tokel Version]) + + BITCOIN_DAEMON_NAME=komodod BITCOIN_CLI_NAME=komodo-cli BITCOIN_TX_NAME=komodo-tx @@ -195,11 +206,19 @@ AC_ARG_ENABLE([werror], [enable_werror=$enableval], [enable_werror=no]) +# Enable websockets listener +AC_ARG_ENABLE([websockets], + [AS_HELP_STRING([--enable-websockets], + [enable websockets (default is no)])], + [enable_websockets=$enableval], + [enable_websockets=no]) + AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-Werror],[CXXFLAG_WERROR="-Werror"],[CXXFLAG_WERROR=""]) if test "x$enable_debug" = xyes; then - CPPFLAGS="$CPPFLAGS -DDEBUG -DDEBUG_LOCKORDER" +dnl CPPFLAGS="$CPPFLAGS -DDEBUG -DDEBUG_LOCKORDER" + CPPFLAGS="$CPPFLAGS -DDEBUG" if test "x$GCC" = xyes; then CFLAGS="$CFLAGS -g3 -O0" fi @@ -612,6 +631,17 @@ else AC_DEFINE(ENABLE_PROTON, 0, [Define to 1 to enable Proton functions]) fi +dnl enable websockets +AC_MSG_CHECKING([if websockets should be enabled]) +if test x$enable_websockets != xno; then + AC_MSG_RESULT(no) + # AC_MSG_RESULT(yes) + # disable websockets for full code audit + # AC_DEFINE_UNQUOTED([ENABLE_WEBSOCKETS],[1],[Define to 1 to enable websockets listener]) +else + AC_MSG_RESULT(no) +fi + if test x$build_bitcoin_utils$build_bitcoind$use_tests = xnonono; then use_boost=no else @@ -905,6 +935,7 @@ AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes]) AM_CONDITIONAL([EXPERIMENTAL_ASM],[test x$experimental_asm = xyes]) AM_CONDITIONAL([ASAN],[test x$use_asan = xyes]) AM_CONDITIONAL([TSAN],[test x$use_tsan = xyes]) +AM_CONDITIONAL([ENABLE_WEBSOCKETS],[test x$enable_websockets = xyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version]) @@ -975,6 +1006,46 @@ PKG_CONFIG_LIBDIR="$PKGCONFIG_LIBDIR_TEMP" ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery" AC_CONFIG_SUBDIRS([src/secp256k1 src/snark src/univalue src/cryptoconditions]) +# build custom executable with customised args: + +AC_ARG_WITH([custom-bin], + [AS_HELP_STRING([--with-custom-bin], + [build custom named daemon cli (default=no)])], + [build_custom_bin=$withval], + [build_custom_bin=no]) + +# build custom executable added with an Makefile.nnn.include +AM_CONDITIONAL([BUILD_CUSTOM_BIN], [test "x$build_custom_bin" = "xyes"]) + +AC_ARG_VAR(CUSTOM_BIN_NAME, [custom executable prefix]) +if test "x${CUSTOM_BIN_NAME+set}" = "xset"; then + AC_SUBST([CUSTOM_BIN_NAME], ${CUSTOM_BIN_NAME}) +else + AC_SUBST([CUSTOM_BIN_NAME], '') +fi + +AC_ARG_VAR(CUSTOM_BRAND_NAME, [custom brand name]) +if test "x${CUSTOM_BRAND_NAME+set}" = "xset"; then + AC_SUBST([CUSTOM_BRAND_NAME], ${CUSTOM_BRAND_NAME}) +else + AC_SUBST([CUSTOM_BRAND_NAME], '') +fi + +AC_ARG_VAR(CUSTOM_SERVER_ARGS, [custom daemon params]) +if test "x${CUSTOM_SERVER_ARGS+set}" = "xset"; then + AC_SUBST([CUSTOM_SERVER_ARGS], ${CUSTOM_SERVER_ARGS}) +else + AC_SUBST([CUSTOM_SERVER_ARGS], '') +fi + +AC_ARG_VAR(CUSTOM_CLIENT_ARGS, [custom cli params]) +if test "x${CUSTOM_CLIENT_ARGS+set}" = "xset"; then + AC_SUBST([CUSTOM_CLIENT_ARGS], ${CUSTOM_CLIENT_ARGS}) +else + AC_SUBST([CUSTOM_CLIENT_ARGS], '') +fi + + AC_OUTPUT dnl Taken from https://wiki.debian.org/RpathIssue @@ -996,6 +1067,7 @@ echo " with zmq = $use_zmq" echo " with test = $use_tests" echo " debug enabled = $enable_debug" echo " werror = $enable_werror" +echo " with websockets = $enable_websockets" echo echo " target os = $TARGET_OS" echo " build os = $BUILD_OS" diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk index 7be744aebc0..10999dac006 100644 --- a/depends/hosts/darwin.mk +++ b/depends/hosts/darwin.mk @@ -8,10 +8,10 @@ darwin_CXX=g++-8 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysro darwin_CFLAGS=-pipe darwin_CXXFLAGS=$(darwin_CFLAGS) -darwin_release_CFLAGS=-O1 +darwin_release_CFLAGS=-g -O1 darwin_release_CXXFLAGS=$(darwin_release_CFLAGS) -darwin_debug_CFLAGS=-O1 +darwin_debug_CFLAGS=-g -O0 darwin_debug_CXXFLAGS=$(darwin_debug_CFLAGS) darwin_native_toolchain=native_cctools diff --git a/depends/hosts/linux.mk b/depends/hosts/linux.mk index 31748d66226..a141178a3dd 100644 --- a/depends/hosts/linux.mk +++ b/depends/hosts/linux.mk @@ -1,10 +1,10 @@ linux_CFLAGS=-pipe linux_CXXFLAGS=$(linux_CFLAGS) -linux_release_CFLAGS=-O1 +linux_release_CFLAGS=-g -O2 linux_release_CXXFLAGS=$(linux_release_CFLAGS) -linux_debug_CFLAGS=-O1 +linux_debug_CFLAGS=-g -O0 linux_debug_CXXFLAGS=$(linux_debug_CFLAGS) linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC diff --git a/depends/hosts/mingw32.mk b/depends/hosts/mingw32.mk index b217bfdb547..1f74eec23ae 100644 --- a/depends/hosts/mingw32.mk +++ b/depends/hosts/mingw32.mk @@ -1,12 +1,12 @@ mingw32_CC=x86_64-w64-mingw32-gcc-posix mingw32_CXX=x86_64-w64-mingw32-g++-posix mingw32_CFLAGS=-pipe -std=c11 -mingw32_CXXFLAGS=$(mingw32_CFLAGS) -std=c++11 +mingw32_CXXFLAGS=-pipe -std=c++11 -mingw32_release_CFLAGS=-O1 +mingw32_release_CFLAGS=-g -O2 mingw32_release_CXXFLAGS=$(mingw32_release_CFLAGS) -mingw32_debug_CFLAGS=-O1 +mingw32_debug_CFLAGS=-g -O0 mingw32_debug_CXXFLAGS=$(mingw32_debug_CFLAGS) mingw32_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 3981e98d203..ee8ccb269e7 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -4,6 +4,7 @@ $(package)_version=1_72_0 $(package)_download_path=https://github.com/KomodoPlatform/boost/releases/download/boost-1.72.0-kmd $(package)_sha256_hash=59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722 $(package)_file_name=$(package)_$($(package)_version).tar.bz2 +$(package)_patches=commit-74fb0a2.patch commit-f9d0e59.patch define $(package)_set_vars $(package)_config_opts_release=variant=release @@ -27,6 +28,8 @@ endef define $(package)_preprocess_cmds + patch -p2 -i $($(package)_patch_dir)/commit-74fb0a2.patch && \ + patch -p2 -i $($(package)_patch_dir)/commit-f9d0e59.patch && \ echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cxxflags) $($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$(boost_archiver_$(host_os))\" \"$(host_STRIP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;" > user-config.jam endef diff --git a/depends/packages/openssl.mk b/depends/packages/openssl.mk index 296a553894f..351735010d1 100644 --- a/depends/packages/openssl.mk +++ b/depends/packages/openssl.mk @@ -1,8 +1,8 @@ package=openssl -$(package)_version=1.1.1f +$(package)_version=1.1.1k $(package)_download_path=https://www.openssl.org/source $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=186c6bfe6ecfba7a5b48c47f8a1673d0f3b0e5ba2e25602dd23b629975da3f35 +$(package)_sha256_hash=892a0875b9872acd04a9fde79b1f943075d5ea162415de3047c327df33fbaee5 define $(package)_set_vars $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" @@ -62,7 +62,7 @@ $(package)_config_opts+=no-srtp $(package)_config_opts+=no-ssl3 $(package)_config_opts+=no-ssl3-method $(package)_config_opts+=no-ssl-trace -$(package)_config_opts+=no-stdio +# $(package)_config_opts+=no-stdio $(package)_config_opts+=no-tls1 $(package)_config_opts+=no-tls1-method $(package)_config_opts+=no-ts diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 1c1a506689f..480540416bb 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -47,8 +47,8 @@ native_packages := native_ccache wallet_packages=bdb ifeq ($(host_os),linux) - packages := boost openssl libevent zeromq $(zcash_packages) googletest libcurl #googlemock + packages := boost openssl libevent zeromq $(zcash_packages) googletest libcurl # websocketspp #googlemock else - packages := boost openssl libevent zeromq $(zcash_packages) libcurl googletest #googlemock + packages := boost openssl libevent zeromq $(zcash_packages) libcurl googletest # websocketspp #googlemock endif diff --git a/depends/packages/websocketspp.mk b/depends/packages/websocketspp.mk new file mode 100644 index 00000000000..0e1bfa03c14 --- /dev/null +++ b/depends/packages/websocketspp.mk @@ -0,0 +1,11 @@ +package=websocketpp +$(package)_version=0.8.2 +$(package)_download_path=https://github.com/zaphoyd/websocketpp/archive +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=6ce889d85ecdc2d8fa07408d6787e7352510750daa66b5ad44aacb47bea76755 +$(package)_download_file=$($(package)_version).tar.gz + +define $(package)_stage_cmds + mkdir $($(package)_staging_dir)$(host_prefix)/include && \ + cp -a ./websocketpp $($(package)_staging_dir)$(host_prefix)/include +endef diff --git a/depends/patches/boost/commit-74fb0a2.patch b/depends/patches/boost/commit-74fb0a2.patch new file mode 100644 index 00000000000..4efe81fb00e --- /dev/null +++ b/depends/patches/boost/commit-74fb0a2.patch @@ -0,0 +1,19 @@ +From 74fb0a26099bc51d717f5f154b37231ce7df3e98 Mon Sep 17 00:00:00 2001 +From: Rob Boehne +Date: Wed, 20 Nov 2019 11:25:20 -0600 +Subject: Revert change to elide a warning that caused Solaris builds to fail. + + +diff --git a/include/boost/thread/pthread/thread_data.hpp b/include/boost/thread/pthread/thread_data.hpp +index aefbeb43..bc9b1367 100644 +--- a/include/boost/thread/pthread/thread_data.hpp ++++ b/include/boost/thread/pthread/thread_data.hpp +@@ -57,7 +57,7 @@ namespace boost + #else + std::size_t page_size = ::sysconf( _SC_PAGESIZE); + #endif +-#if PTHREAD_STACK_MIN > 0 ++#ifdef PTHREAD_STACK_MIN + if (size +Date: Tue, 10 Aug 2021 14:22:28 +0000 +Subject: Fix -Wsign-compare warning with glibc 2.34 on Linux platforms. + +In file included from /data/mwrep/res/osp/Boost/21-0-0-0/include/boost/thread/thread_only.hpp:17, + from /data/mwrep/res/osp/Boost/21-0-0-0/include/boost/thread/thread.hpp:12, + from src/GetTest.cpp:12: +/data/mwrep/res/osp/Boost/21-0-0-0/include/boost/thread/pthread/thread_data.hpp: In member function 'void boost::thread_attributes::set_stack_size(std::size_t)': +/data/mwrep/res/osp/Boost/21-0-0-0/include/boost/thread/pthread/thread_data.hpp:61:19: error: comparison of integer expressions of different signedness: 'std::size_t' {aka 'long unsigned int'} and 'long int' [-Werror=sign-compare] + 61 | if (size(PTHREAD_STACK_MIN)) size=PTHREAD_STACK_MIN; + #endif + size = ((size+page_size-1)/page_size)*page_size; + int res = pthread_attr_setstacksize(&val_, size); \ No newline at end of file diff --git a/qa/pytest_komodo/basic/pytest_util.py b/qa/pytest_komodo/basic/pytest_util.py index 27af5501ef0..b673fd59718 100644 --- a/qa/pytest_komodo/basic/pytest_util.py +++ b/qa/pytest_komodo/basic/pytest_util.py @@ -80,12 +80,12 @@ def mine_and_waitconfirms(txid, proxy, confs_req=2): # should be used after tx try: confirmations_amount = proxy.getrawtransaction(txid, 1)['confirmations'] if confirmations_amount < confs_req: - print("\ntx is not confirmed yet! Let's wait a little more") + print("\ntx is not confirmed yet! Let's wait a little more", txid, 'confs=', confirmations_amount, 'required=', confs_req) time.sleep(5) else: print("\ntx confirmed") return True - except KeyError as e: + except (KeyError, RPCError) as e: print("\ntx is in mempool still probably, let's wait a little bit more\nError: ", e) time.sleep(5) attempts += 1 @@ -249,7 +249,7 @@ def validate_tx_pattern(txid): def validate_raddr_pattern(addr): if not isinstance(addr, str): return False - address_pattern = re.compile(r"R[a-zA-Z0-9]{33}\Z") + address_pattern = re.compile(r"[RC][a-zA-Z0-9]{33}\Z") if address_pattern.match(addr): return True else: diff --git a/qa/pytest_komodo/basic/test_blocks.py b/qa/pytest_komodo/basic/test_blocks.py index 4117421930a..99d0bfafa1f 100644 --- a/qa/pytest_komodo/basic/test_blocks.py +++ b/qa/pytest_komodo/basic/test_blocks.py @@ -342,6 +342,8 @@ def test_getrawmempool(self, test_params): res = rpc.getrawmempool(True) assert res.get(txid).get('height') == kvheight + ''' + # kv not supported def test_kvsearch(self, test_params): test_values = { 'key': 'search_key', @@ -358,6 +360,7 @@ def test_kvsearch(self, test_params): assert res.get('key') == test_values['key'] assert res.get('keylen') == keylen assert res.get('value') == test_values['value'] + ''' def test_notaries(self, test_params): rpc = test_params.get('node1').get('rpc') diff --git a/qa/pytest_komodo/cc_modules/test_faucet.py b/qa/pytest_komodo/cc_modules/test_faucet.py index 876c69bf89b..7169fb32cf2 100644 --- a/qa/pytest_komodo/cc_modules/test_faucet.py +++ b/qa/pytest_komodo/cc_modules/test_faucet.py @@ -100,7 +100,7 @@ class TestFaucetCCe2e: def test_faucet_addresses(self, test_params): rpc1 = test_params.get('node1').get('rpc') pubkey = test_params.get('node1').get('pubkey') - address_pattern = re.compile(r"R[a-zA-Z0-9]{33}\Z") # normal R-addr + address_pattern = re.compile(r"[RC][a-zA-Z0-9]{33}\Z") # normal R-addr res = rpc1.faucetaddress() for key in res.keys(): diff --git a/qa/pytest_komodo/cc_modules/test_heir.py b/qa/pytest_komodo/cc_modules/test_heir.py index a5659cf9a5f..bac1c33e9fa 100644 --- a/qa/pytest_komodo/cc_modules/test_heir.py +++ b/qa/pytest_komodo/cc_modules/test_heir.py @@ -189,7 +189,7 @@ class TestHeirFunc: def test_heir_addresses(self, test_params): rpc1 = test_params.get('node1').get('rpc') pubkey = test_params.get('node2').get('pubkey') - address_pattern = re.compile(r"R[a-zA-Z0-9]{33}\Z") # normal R-addr + address_pattern = re.compile(r"[RC][a-zA-Z0-9]{33}\Z") # normal R-addr # verify all keys look like valid AC addrs res = rpc1.faucetaddress('') diff --git a/qa/pytest_komodo/cc_modules/test_oracles.py b/qa/pytest_komodo/cc_modules/test_oracles.py index 8c971614b8c..8b7a5ce3da8 100644 --- a/qa/pytest_komodo/cc_modules/test_oracles.py +++ b/qa/pytest_komodo/cc_modules/test_oracles.py @@ -8,7 +8,7 @@ import time from util import assert_success, assert_error, mine_and_waitconfirms,\ send_and_mine, rpc_connect, wait_some_blocks, generate_random_string, komodo_teardown - +from slickrpc.exc import RpcException @pytest.mark.usefixtures("proxy_connection") def test_oracles(test_params): @@ -27,14 +27,14 @@ def test_oracles(test_params): for x in result.keys(): if x.find('ddress') > 0: - assert result[x][0] == 'R' + assert result[x][0] == 'R' or result[x][0] == 'C' result = rpc.oraclesaddress(pubkey) assert_success(result) for x in result.keys(): if x.find('ddress') > 0: - assert result[x][0] == 'R' + assert result[x][0] == 'R' or result[x][0] == 'C' # there are no oracles created yet if is_fresh_chain: @@ -73,8 +73,12 @@ def test_oracles(test_params): list_fund_txid = [] for f in valid_formats: # trying to register with negative datafee - result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "-100") - assert_error(result) + try : + result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "-100") + assert_error(result) + except RpcException as e : # catching AmountFromValue(-100) exception + print('got normal exception', e) + pass # trying to register with zero datafee result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "0") @@ -243,6 +247,7 @@ def test_oracles(test_params): # checking data for s type result = rpc.oraclessamples(globals()["oracle_{}".format("s")], batonaddr_s, "1") + print('result=', result) assert "['Anton']" == str(result["samples"][0]['data']) # checking data for S type diff --git a/qa/pytest_komodo/cc_modules/test_token.py b/qa/pytest_komodo/cc_modules/test_token.py index 5e02c1b5fb0..462d3a34b23 100644 --- a/qa/pytest_komodo/cc_modules/test_token.py +++ b/qa/pytest_komodo/cc_modules/test_token.py @@ -30,29 +30,24 @@ def test_token(test_params): for v in ["", "v2"] : - result = call_token_rpc(rpc, "token"+v+"address") - assert_success(result) - for x in result.keys(): - if x.find('ddress') > 0: - assert result[x][0] == 'R' - - result = call_token_rpc(rpc, "token"+v+"address", pubkey) - assert_success(result) - for x in result.keys(): - if x.find('ddress') > 0: - assert result[x][0] == 'R' - - result = call_token_rpc(rpc, "assetsaddress") - assert_success(result) - for x in result.keys(): - if x.find('ddress') > 0: - assert result[x][0] == 'R' - - result = call_token_rpc(rpc, "assetsaddress", pubkey) - assert_success(result) - for x in result.keys(): - if x.find('ddress') > 0: - assert result[x][0] == 'R' + result = call_token_rpc(rpc, "token"+v+"indexkey", pubkey) + if v == '': # for v2 a diff result + assert_success(result) + for x in result.keys(): + if x.find('ddress') > 0: + assert result[x][0] == 'R' or result[x][0] == 'C' + else : # v2 index keys: + for x in result: + assert x[0] == 'C' + + result = call_token_rpc(rpc, "assets"+v+"indexkey", pubkey) + if v == '': # for v2 a diff result + assert_success(result) + for x in result.keys(): + if x.find('ddress') > 0: + assert result[x][0] == 'R' or result[x][0] == 'C' + else : # v2 index key + assert result[0] == 'C' # there are no tokens created yet # TODO: this test conflicts with heir test because token creating for heir @@ -186,8 +181,12 @@ def test_token(test_params): assert result > 0.1 print("making invalid node token" + v + "cancelask(s)...") - result = call_token_rpc(rpc1, "token"+v+"cancelask", tokenid, testorderid) - assert_error(result) + badcancel = call_token_rpc(rpc1, "token"+v+"cancelask", tokenid, testorderid) + try : + rpc1.sendrawtransaction(badcancel["hex"]) # checked at consensus + assert False, "order should not be cancelled with another pk" # should be exception + except RPCError : + pass # exception is normal here print("making valid node token" + v + "cancelask...") # from valid node diff --git a/qa/pytest_komodo/cc_modules/test_unspentccundex.py b/qa/pytest_komodo/cc_modules/test_unspentccundex.py index bf72dd61793..9e50074f724 100644 --- a/qa/pytest_komodo/cc_modules/test_unspentccundex.py +++ b/qa/pytest_komodo/cc_modules/test_unspentccundex.py @@ -28,7 +28,7 @@ def test_ccindex(test_params): assert_success(result) for x in result.keys(): if x.find('ddress') > 0: - assert result[x][0] == 'R' + assert result[x][0] == 'R' or result[x][0] == 'C' # get token cc address for pubkey: result = rpc.tokenaddress(pubkey) @@ -36,7 +36,7 @@ def test_ccindex(test_params): pubkeyTokenCCAddress = "" for x in result.keys(): if x.find('ddress') > 0: - assert result[x][0] == 'R' + assert result[x][0] == 'R' or result[x][0] == 'C' if x == 'pubkey Tokens CC Address': pubkeyTokenCCAddress = result[x] @@ -46,7 +46,7 @@ def test_ccindex(test_params): pubkey1TokenCCAddress = "" for x in result.keys(): if x.find('ddress') > 0: - assert result[x][0] == 'R' + assert result[x][0] == 'R' or result[x][0] == 'C' if x == 'pubkey Tokens CC Address': pubkey1TokenCCAddress = result[x] diff --git a/qa/pytest_komodo/cc_modules/util.py b/qa/pytest_komodo/cc_modules/util.py index 001817b1cdf..a1d5ea33e34 100644 --- a/qa/pytest_komodo/cc_modules/util.py +++ b/qa/pytest_komodo/cc_modules/util.py @@ -5,9 +5,10 @@ from string import ascii_uppercase try: from slickrpc import Proxy + from slickrpc.exc import RpcException as RPCError except ImportError: from bitcoinrpc.authproxy import AuthServiceProxy as Proxy - + from bitcoinrpc.authproxy import JSONRPCException as RPCError def assert_success(result): assert result['result'] == 'success' @@ -34,12 +35,12 @@ def mine_and_waitconfirms(txid, proxy, confs_req=2): # should be used after tx try: confirmations_amount = proxy.getrawtransaction(txid, 1)['confirmations'] if confirmations_amount < confs_req: - print("\ntx is not confirmed yet! Let's wait a little more") + print("\ntx is not confirmed yet! Let's wait a little more", txid, 'confs=', confirmations_amount, 'required=', confs_req) time.sleep(5) else: print("\ntx confirmed") return True - except KeyError as e: + except (KeyError, RPCError) as e: print("\ntx is in mempool still probably, let's wait a little bit more\nError: ", e) time.sleep(5) attempts += 1 diff --git a/qa/pytest_komodo/chainconfig.json b/qa/pytest_komodo/chainconfig.json index 233211860b8..73350c9c2cb 100644 --- a/qa/pytest_komodo/chainconfig.json +++ b/qa/pytest_komodo/chainconfig.json @@ -3,8 +3,8 @@ "rpc_user": "test", "rpcpassword": "test", "rpcallowip": "0.0.0.0/0", - "rpcport": 7000, - "port": 6000, + "rpcport": 7001, + "port": 6001, "rpcbind": "0.0.0.0", "ac_name": "TONYCI", "ac_reward": "100000000000", diff --git a/qa/pytest_komodo/chainstart.py b/qa/pytest_komodo/chainstart.py index eeebb6cf350..b49548daafa 100644 --- a/qa/pytest_komodo/chainstart.py +++ b/qa/pytest_komodo/chainstart.py @@ -81,7 +81,7 @@ def create_configs(asset, node=0): 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('rpcport=' + str(7001 + node) + '\n') conf.write("rpcbind=0.0.0.0\n") conf.write("rpcallowip=0.0.0.0/0\n") @@ -149,7 +149,7 @@ def main(): 'rpc_user': 'test', 'rpc_password': 'test', 'rpc_ip': '127.0.0.1', - 'rpc_port': 7000 + i + 'rpc_port': 7001 + i } rpc_p = create_proxy(node_params) validate_proxy(env_params, rpc_p, i) diff --git a/qa/pytest_komodo/nodesconfig.json b/qa/pytest_komodo/nodesconfig.json index df8792e4c26..1141da27196 100644 --- a/qa/pytest_komodo/nodesconfig.json +++ b/qa/pytest_komodo/nodesconfig.json @@ -3,8 +3,8 @@ "rpc_user" : "test", "rpc_password" : "test", "rpc_ip" : "127.0.0.1", - "rpc_port": 7000, - "net_port": 6000, + "rpc_port": 7001, + "net_port": 6001, "pubkey" : "02f0ec2d3da51b09e4fc8d9ba334c275b02b3ab6f22ce7be0ea5059cbccbd1b8c7", "address": "RPWhA4f4ZTZxNi5K36bcwsWdVjSVDSjUnd", "txid": "e9945094eb1a9028afd310719aea21966425ceccc3f8c0972277a96201ca1b81" @@ -13,8 +13,8 @@ "rpc_user" : "test", "rpc_password" : "test", "rpc_ip" : "127.0.0.1", - "rpc_port": 7001, - "net_port": 6001, + "rpc_port": 7002, + "net_port": 6002, "pubkey" : "0285f68aec0e2f8b5e817d71a2a20a1fda74ea9943c752a13136a3a30fa49c0149", "address": "RHoTHYiHD8TU4k9rbY4Aoj3ztxUARMJikH", "txid": "3376a48a7daf543d8401e072102d4e93f53f300a101bd67e82820d2da0f2729e" @@ -23,8 +23,8 @@ "rpc_user" : "test", "rpc_password" : "test", "rpc_ip" : "127.0.0.1", - "rpc_port": 7002, - "net_port": 6002, + "rpc_port": 7003, + "net_port": 6003, "pubkey" : "02e9b141e1c251a942f77df10fa4de00f53f8ab2a6d5341bbaf842c95e674e92e9", "address": "RB6fgrPX4ANGTtMUVud6aRh5cFy7TqVein", "txid": "3376a48a7daf543d8401e072102d4e93f53f300a101bd67e82820d2da0f2729e" diff --git a/src/Makefile.am b/src/Makefile.am index 8741dc856db..b96faa2554e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ -DIST_SUBDIRS = secp256k1 univalue cryptoconditions - +DIST_SUBDIRS = secp256k1 univalue +SUBDIRS = cryptoconditions AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(SAN_LDFLAGS) $(HARDENED_LDFLAGS) AM_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) AM_CPPFLAGS = $(HARDENED_CPPFLAGS) @@ -15,7 +15,7 @@ LIBMEMENV += $(builddir)/leveldb/libmemenv.a $(LIBLEVELDB): $(LIBMEMENV) $(LIBLEVELDB) $(LIBMEMENV): - @echo "Building LevelDB ..." && $(MAKE) -C $(@D) $(@F) CXX="$(CXX)" \ + $(AM_V_at)$(MAKE) -C $(@D) $(@F) CXX="$(CXX)" \ CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \ OPT="$(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -D__STDC_LIMIT_MACROS" endif @@ -31,16 +31,8 @@ BITCOIN_INCLUDES += -I$(srcdir)/snark BITCOIN_INCLUDES += -I$(srcdir)/snark/libsnark BITCOIN_INCLUDES += -I$(srcdir)/univalue/include -if TARGET_WINDOWS -LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl -endif -if TARGET_DARWIN -LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl -endif -if TARGET_LINUX -LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl -endif - +LIBBITCOIN_SERVER=libbitcoin_server.a +LIBBITCOIN_WALLET=libbitcoin_wallet.a LIBBITCOIN_COMMON=libbitcoin_common.a LIBBITCOIN_CLI=libbitcoin_cli.a LIBBITCOIN_UTIL=libbitcoin_util.a @@ -48,9 +40,10 @@ LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBVERUS_CRYPTO=crypto/libverus_crypto.a LIBVERUS_PORTABLE_CRYPTO=crypto/libverus_portable_crypto.a LIBSECP256K1=secp256k1/libsecp256k1.la -LIBCRYPTOCONDITIONS=cryptoconditions/libcryptoconditions_core.la +LIBCRYPTOCONDITIONS=cryptoconditions/libcryptoconditions_core.a LIBSNARK=snark/libsnark.a LIBUNIVALUE=univalue/libunivalue.la +LIBCC=libcc.a LIBZCASH=libzcash.a if ENABLE_ZMQ @@ -62,15 +55,10 @@ endif if BUILD_BITCOIN_LIBS LIBZCASH_CONSENSUS=libzcashconsensus.la endif -if ENABLE_WALLET -LIBBITCOIN_WALLET=libbitcoin_wallet.a -endif - -$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) - $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -march=x86-64 -g " LIBSNARK_CXXFLAGS = $(AM_CXXFLAGS) $(PIC_FLAGS) -DBINARY_OUTPUT -DNO_PT_COMPRESSION=1 -fstack-protector-all LIBSNARK_CONFIG_FLAGS = CURVE=ALT_BN128 NO_PROCPS=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT NO_COPY_DEPINST=1 NO_COMPILE_LIBGTEST=1 +LIBSNARK_OPTFLAGS = $(CPPFLAGS) -march=x86-64 if HAVE_OPENMP LIBSNARK_CONFIG_FLAGS += MULTICORE=1 endif @@ -78,17 +66,24 @@ if TARGET_DARWIN LIBSNARK_CONFIG_FLAGS += PLATFORM=darwin endif +$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="$(LIBSNARK_OPTFLAGS) " + $(LIBSNARK): $(wildcard snark/src/*) - $(AM_V_at) CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64" + $(AM_V_at)CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="$(LIBSNARK_OPTFLAGS)" libsnark-tests: $(wildcard snark/src/*) - $(AM_V_at) CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ check DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64" + $(AM_V_at)CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ check DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="$(LIBSNARK_OPTFLAGS)" $(LIBUNIVALUE): $(wildcard univalue/lib/*) - $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -march=x86-64 -g " + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="$(LIBSNARK_OPTFLAGS)" -$(LIBCRYPTOCONDITIONS): $(wildcard cryptoconditions/src/*) $(wildcard cryptoconditions/include/*) - $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -march=x86-64 -g " +# libcjson build +LIBCJSON=libcjson.a +libcjson_a_SOURCES = cJSON.c \ + komodo_cJSON.c komodo_cutils.cpp +libcjson_a_CPPFLAGS=-fPIC +EXTRA_LIBRARIES += $(LIBCJSON) # Make is not made aware of per-object dependencies to avoid limiting building parallelization # But to build the less dependent modules first, we manually select their order here: @@ -112,7 +107,10 @@ if ENABLE_PROTON EXTRA_LIBRARIES += $(LIBBITCOIN_PROTON) endif -lib_LTLIBRARIES = $(LIBZCASH_CONSENSUS) +lib_LTLIBRARIES = $(LIBZCASH_CONSENSUS) $(LIBCC) + +LIBSSLSTATIC = $(abs_builddir)/../depends/$(host)/lib/libssl.a +LIBCRYPTOSTATIC = $(abs_builddir)/../depends/$(host)/lib/libcrypto.a bin_PROGRAMS = noinst_PROGRAMS = @@ -269,6 +267,9 @@ BITCOIN_CORE_H = \ zmq/zmqnotificationinterface.h \ zmq/zmqpublishnotifier.h +if ENABLE_WEBSOCKETS + BITCOIN_CORE_H += komodo_websockets.h +endif obj/build.h: FORCE @$(MKDIR_P) $(builddir)/obj @@ -276,9 +277,10 @@ obj/build.h: FORCE $(abs_top_srcdir) libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h -# server: zcashd +# server: komodod libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +libbitcoin_server_a_CPPFLAGS += -fPIC libbitcoin_server_a_SOURCES = \ sendalert.cpp \ addrman.cpp \ @@ -291,23 +293,20 @@ libbitcoin_server_a_SOURCES = \ cc/import.cpp \ cc/importgateway.cpp \ cc/CCassetsUtils.cpp \ - cc/old/CCassetsCore_v0.cpp \ - cc/old/CCassetstx_v0.cpp \ cc/CCcustom.cpp \ cc/CCtx.cpp \ cc/CCutils.cpp \ cc/CCvalidation.cpp \ + cc/CCtokens.h \ + cc/CCtokens_impl.h \ 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 \ @@ -318,7 +317,8 @@ libbitcoin_server_a_SOURCES = \ cc/betprotocol.cpp \ cc/pricesfeed.cpp \ cc/priceslibs/cjsonpointer.cpp \ - cc/CCNFTData.cpp \ + cc/CCTokelData.h \ + cc/CCTokelData.cpp \ chain.cpp \ checkpoints.cpp \ fs.cpp \ @@ -366,6 +366,10 @@ libbitcoin_server_a_SOURCES = \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) +if ENABLE_WEBSOCKETS + libbitcoin_server_a_SOURCES += komodo_websockets.cpp +endif + if ENABLE_ZMQ libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS) libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -411,6 +415,11 @@ libbitcoin_wallet_a_SOURCES = \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) +# a shared library for cryptoconditions +libcc_a_SOURCES = cc/cclib.cpp +libcc_a_CXXFLAGS = -DBUILD_CUSTOMCC -I../secp256k1/include -I../depends/$(shell echo `../depends/config.guess`/include) -I./univalue/include -I./cryptoconditions/include -I./cryptoconditions/src -I./cryptoconditions/src/asn -I. -I./cc +libcc_a_LDFLAGS = -version-info 0:0:0 + # crypto primitives library crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_CONFIG_INCLUDES) crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -506,8 +515,10 @@ libbitcoin_common_a_SOURCES = \ script/standard.cpp \ transaction_builder.cpp \ cc/CCtokenutils.cpp \ - cc/old/CCtokenutils_v0.cpp \ cc/CCutilbits.cpp \ + cc/CCupgrades.h \ + cc/CCupgrades.cpp \ + komodo_version.h \ gmp_i64.c \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) @@ -587,8 +598,6 @@ endif komodod_LDADD += \ $(BOOST_LIBS) \ $(BDB_LIBS) \ - $(SSL_LIBS) \ - $(CRYPTO_LIBS) \ $(EVENT_PTHREADS_LIBS) \ $(EVENT_LIBS) \ $(ZMQ_LIBS) \ @@ -596,16 +605,19 @@ komodod_LDADD += \ $(LIBBITCOIN_CRYPTO) \ $(LIBVERUS_CRYPTO) \ $(LIBVERUS_PORTABLE_CRYPTO) \ - $(LIBZCASH_LIBS) + $(LIBZCASH_LIBS) \ + $(LIBCC) \ + -lcurl -if TARGET_DARWIN -komodod_LDADD += libcc.dylib $(LIBSECP256K1) -endif -if TARGET_WINDOWS -komodod_LDADD += libcc.dll $(LIBSECP256K1) -endif -if TARGET_LINUX -komodod_LDADD += libcc.so $(LIBSECP256K1) +if ENABLE_WEBSOCKETS +# link statically openssl +komodod_LDADD += \ + $(LIBSSLSTATIC) \ + $(LIBCRYPTOSTATIC) +else +komodod_LDADD += \ + $(SSL_LIBS) \ + $(CRYPTO_LIBS) endif if ENABLE_PROTON @@ -630,7 +642,7 @@ endif if ENABLE_WALLET wallet_utility_SOURCES = wallet-utility.cpp wallet_utility_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -wallet_utility_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +wallet_utility_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) wallet_utility_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) endif @@ -695,7 +707,6 @@ komodo_tx_LDADD = \ $(LIBCRYPTOCONDITIONS) komodo_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) -# # zcash protocol primitives # libzcash_a_SOURCES = \ @@ -715,13 +726,12 @@ libzcash_a_SOURCES = \ zcash/circuit/prfs.tcc \ zcash/circuit/utils.tcc -libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) $(HARDENED_CXXFLAGS) $(HARDENED_LDFLAGS) -pipe $(SAN_LDFLAGS) -O1 -g -Wstack-protector $(SAN_CXXFLAGS) -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES) - -#libzcash_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -#libzcash_a_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -#libzcash_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMONTGOMERY_OUTPUT - -libzcash_a_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing +libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 \ + -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) \ + $(HARDENED_CXXFLAGS) $(HARDENED_LDFLAGS) -pipe $(SAN_LDFLAGS) \ + -Wstack-protector $(SAN_CXXFLAGS) -fstack-protector-all -fPIE -fvisibility=hidden \ + -DSTATIC $(BITCOIN_INCLUDES) +libzcash_a_CXXFLAGS = $(CXXFLAGS) $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing libzcash_a_LDFLAGS = $(SAN_LDFLAGS) $(HARDENED_LDFLAGS) libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT @@ -771,7 +781,15 @@ clean-local: -$(MAKE) -C cryptoconditions clean rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno -rm -f config.h - rm -f cc/customcc.so + -rm -f cc/customcc.so cc/customcc.dll cc/customcc.dylib # clean now unused file + -rm -f libcc.so libcc.dll libcc.dylib # clean now unused file + -$(RM) *.a + -$(RM) crypto/*.a + -$(RM) cryptoconditions/.libs/*.a + +clean-all: clean-local + -$(MAKE) -C snark clean-all + -$(MAKE) -C univalue clean-all .rc.o: @test -f $(WINDRES) @@ -799,6 +817,100 @@ endif if ENABLE_TESTS include Makefile.ktest.include +include Makefile.kcctest.include #include Makefile.test.include #include Makefile.gtest.include endif + +# build custom executables with customised params +if BUILD_CUSTOM_BIN +# include Makefile.tokel.include + +# custom server lib compiled with -DCUSTOM_BIN_NAME -DCUSTOM_SERVER_ARGS +LIBCUSTOM_SERVER=libcustom_server.a -lcurl +libcustom_server_a_CPPFLAGS = $(libbitcoin_server_a_CPPFLAGS) -DCUSTOM_BIN_NAME=\"$(CUSTOM_BIN_NAME)\" -DCUSTOM_BRAND_NAME=\"$(CUSTOM_BRAND_NAME)\" -DCUSTOM_SERVER_ARGS=\"$(CUSTOM_SERVER_ARGS)\" +libcustom_server_a_CXXFLAGS = $(libbitcoin_server_a_CXXFLAGS) +libcustom_server_a_SOURCES = $(libbitcoin_server_a_SOURCES) + +EXTRA_LIBRARIES += $(LIBCUSTOM_SERVER) + +bin_PROGRAMS += customd + +# custom daemon binary # +customd_SOURCES = $(komodod_SOURCES) +customd_CPPFLAGS = $(komodod_CPPFLAGS) -DCUSTOM_BIN_NAME=\"$(CUSTOM_BIN_NAME)\" -DCUSTOM_BRAND_NAME=\"$(CUSTOM_BRAND_NAME)\" -DCUSTOM_SERVER_ARGS=\"$(CUSTOM_SERVER_ARGS)\" +customd_CXXFLAGS = $(komodod_CXXFLAGS) +customd_LDFLAGS = $(komodod_LDFLAGS) +customd_LDADD = \ + $(LIBCUSTOM_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_ZMQ) \ + $(LIBBITCOIN_PROTON) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBVERUS_CRYPTO) \ + $(LIBVERUS_PORTABLE_CRYPTO) \ + $(LIBZCASH) \ + $(LIBSNARK) \ + $(LIBLEVELDB) \ + $(LIBMEMENV) \ + $(LIBSECP256K1) \ + $(LIBCRYPTOCONDITIONS) + +if ENABLE_WALLET +customd_LDADD += $(LIBBITCOIN_WALLET) +endif + +customd_LDADD += \ + $(BOOST_LIBS) \ + $(BDB_LIBS) \ + $(SSL_LIBS) \ + $(CRYPTO_LIBS) \ + $(EVENT_PTHREADS_LIBS) \ + $(EVENT_LIBS) \ + $(ZMQ_LIBS) \ + $(PROTON_LIBS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBVERUS_CRYPTO) \ + $(LIBVERUS_PORTABLE_CRYPTO) \ + $(LIBZCASH_LIBS) \ + $(LIBCC) \ + -lcurl + +customd_LDADD += $(LIBDYNCUSTOMCONSENSUS) $(LIBSECP256K1) + +if ENABLE_WEBSOCKETS +# link statically openssl +customd_LDADD += \ + $(LIBSSLSTATIC) \ + $(LIBCRYPTOSTATIC) +else +customd_LDADD += \ + $(SSL_LIBS) \ + $(CRYPTO_LIBS) +endif + +if ENABLE_PROTON +customd_LDADD += $(LIBBITCOIN_PROTON) $(PROTON_LIBS) +endif + +customd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(customd_CXXFLAGS) \ + $(CXXFLAGS) $(customd_LDFLAGS) $(LDFLAGS) -o $(CUSTOM_BIN_NAME)d$(EXEEXT) + + +bin_PROGRAMS += custom-cli + +# custom cli binary # +custom_cli_SOURCES = $(komodo_cli_SOURCES) +custom_cli_CPPFLAGS = $(komodo_cli_CPPFLAGS) -DCUSTOM_BIN_NAME=\"$(CUSTOM_BIN_NAME)\" -DCUSTOM_BRAND_NAME=\"$(CUSTOM_BRAND_NAME)\" -DCUSTOM_CLIENT_ARGS=\"$(CUSTOM_CLIENT_ARGS)\" +custom_cli_CXXFLAGS = $(komodo_cli_CXXFLAGS) +custom_cli_LDFLAGS = $(komodo_cli_LDFLAGS) +custom_cli_LDADD = $(komodo_cli_LDADD) + +custom_cli_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(custom_cli_CXXFLAGS) \ + $(CXXFLAGS) $(custom_cli_LDFLAGS) $(LDFLAGS) -o $(CUSTOM_BIN_NAME)-cli$(EXEEXT) + +endif \ No newline at end of file diff --git a/src/Makefile.kcctest.include b/src/Makefile.kcctest.include new file mode 100644 index 00000000000..7446b395d34 --- /dev/null +++ b/src/Makefile.kcctest.include @@ -0,0 +1,13 @@ +TESTS += komodo-test-cc +bin_PROGRAMS += komodo-test-cc + +# tool for generating our public parameters +komodo_test_cc_SOURCES = \ + test-komodo-cc/test-main.cpp \ + test-komodo-cc/test-assets.cpp + +komodo_test_cc_CPPFLAGS = $(komodod_CPPFLAGS) + +komodo_test_cc_LDADD = -lgtest $(komodod_LDADD) + +komodo_test_cc_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static diff --git a/src/addrman.cpp b/src/addrman.cpp index 10c011028f2..e09034c582d 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -492,12 +492,9 @@ int CAddrMan::Check_() } #endif -void CAddrMan::GetAddr_(std::vector& vAddr) +// gather a list of random nodes, skipping those of low quality +void CAddrMan::GetAddrLimited(std::vector& vAddr, unsigned int nNodes) { - unsigned int nNodes = ADDRMAN_GETADDR_MAX_PCT * vRandom.size() / 100; - if (nNodes > ADDRMAN_GETADDR_MAX) - nNodes = ADDRMAN_GETADDR_MAX; - // gather a list of random nodes, skipping those of low quality for (unsigned int n = 0; n < vRandom.size(); n++) { if (vAddr.size() >= nNodes) @@ -513,6 +510,35 @@ void CAddrMan::GetAddr_(std::vector& vAddr) } } +void CAddrMan::GetAddr_(std::vector& vAddr) +{ + unsigned int nNodes = ADDRMAN_GETADDR_MAX_PCT * vRandom.size() / 100; + if (nNodes > ADDRMAN_GETADDR_MAX) + nNodes = ADDRMAN_GETADDR_MAX; + + GetAddrLimited(vAddr, nNodes); +} + +#ifdef ENABLE_WEBSOCKETS +// get no more than N addresses for clients (do not use 23% limit assuming there are no many websockets listeners in the net) +void CAddrMan::GetAddrAtMost_(std::vector& vAddr) +{ + unsigned int nNodes = vRandom.size(); + if (nNodes > 1000) // actually MAX_ADDR_TO_SEND + nNodes = 1000; + + GetAddrLimited(vAddr, nNodes); +} + +// get all addrinfo for printing +void CAddrMan::GetAddrInfoAll_(std::vector& vAddrInfo) +{ + for (auto const &i : mapInfo) { + vAddrInfo.push_back(i.second); + } +} +#endif + void CAddrMan::Connected_(const CService& addr, int64_t nTime) { CAddrInfo* pinfo = Find(addr); diff --git a/src/addrman.h b/src/addrman.h index 28e07a82b13..2ee814fd58a 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -263,9 +263,17 @@ friend class CAddrManTest; int Check_(); #endif + void GetAddrLimited(std::vector &vAddr, unsigned int nNodes); + //! Select several addresses at once. void GetAddr_(std::vector &vAddr); +#ifdef ENABLE_WEBSOCKETS + //! Actual function to select several at most N addresses at once. + void GetAddrAtMost_(std::vector &vAddr); + void GetAddrInfoAll_(std::vector &vAddrInfo); +#endif + //! Mark an entry as currently-connected-to. void Connected_(const CService &addr, int64_t nTime); @@ -499,9 +507,9 @@ friend class CAddrManTest; Check(); } - void Clear() +private: + void Clear_() { - LOCK(cs); std::vector().swap(vRandom); nKey = GetRandHash(); for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) { @@ -522,9 +530,16 @@ friend class CAddrManTest; mapAddr.clear(); } +public: CAddrMan() { - Clear(); + Clear_(); // use unlocked version bcz locked one caused boost: mutex lock failed in pthread_mutex_lock: Invalid argument exception because of static init concurrency (static dd_mutex has not been constructed yet) + } + + void Clear() + { + LOCK(cs); + Clear_(); } ~CAddrMan() @@ -632,6 +647,40 @@ friend class CAddrManTest; return vAddr; } +#ifdef ENABLE_WEBSOCKETS + //! Return at most N addresses, selected at random. + std::vector GetAddrAtMost() + { + Check(); + std::vector vAddr; + { + LOCK(cs); + GetAddrAtMost_(vAddr); + } + Check(); + return vAddr; + } + + //! Return all addrinfo. + std::vector GetAddrInfoAll() + { + Check(); + std::vector vAddrInfo; + { + LOCK(cs); + GetAddrInfoAll_(vAddrInfo); + } + Check(); + return vAddrInfo; + } + + // for debugging: access to addrinfo private member + CNetAddr GetSource(CAddrInfo addrInfo) + { + return addrInfo.source; + } +#endif + //! Mark an entry as currently-connected-to. void Connected(const CService &addr, int64_t nTime = GetTime()) { diff --git a/src/alert.cpp b/src/alert.cpp index e76f6a41108..d6686bb629b 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -27,6 +27,8 @@ #include "ui_interface.h" #include "util.h" +#include "komodo_version.h" + #include #include #include @@ -137,7 +139,7 @@ bool CAlert::AppliesTo(int nVersion, const std::string& strSubVerIn) const bool CAlert::AppliesToMe() const { - return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); + return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, { TOKEL_CLIENT_NAME })); } bool CAlert::RelayTo(CNode* pnode) const diff --git a/src/alert.h b/src/alert.h index 16204c9c533..cd978c5546f 100644 --- a/src/alert.h +++ b/src/alert.h @@ -124,4 +124,6 @@ class CAlert : public CUnsignedAlert static CAlert getAlertByHash(const uint256 &hash); }; +void ThreadSendAlert(); + #endif // BITCOIN_ALERT_H diff --git a/src/base58.cpp b/src/base58.cpp index 383666d826b..985887303a4 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -230,6 +230,8 @@ class CBitcoinAddressVisitor : public boost::static_visitor bool operator()(const CKeyID& id) const { return addr->Set(id); } bool operator()(const CPubKey& key) const { return addr->Set(key); } bool operator()(const CScriptID& id) const { return addr->Set(id); } + bool operator()(const CCryptoConditionID& id) const { return addr->Set(id); } + bool operator()(const CCLTVID& id) const { return addr->Set(id); } bool operator()(const CNoDestination& no) const { return false; } }; @@ -254,6 +256,21 @@ bool CBitcoinAddress::Set(const CScriptID& id) return true; } +bool CBitcoinAddress::Set(const CCryptoConditionID& id) +{ + SetData(Params().Base58Prefix(CChainParams::CRYPTOCONDITION_ADDRESS), &id, 20); + return true; +} + +bool CBitcoinAddress::Set(const CCLTVID& id) +{ + if (id.which() == TX_PUBKEY) + Set(id.GetPubKey()); + else + Set(id.GetKeyID()); + return true; +} + bool CBitcoinAddress::Set(const CTxDestination& dest) { return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); @@ -268,7 +285,8 @@ bool CBitcoinAddress::IsValid(const CChainParams& params) const { bool fCorrectSize = vchData.size() == 20; bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) || - vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS) || + vchVersion == params.Base58Prefix(CChainParams::CRYPTOCONDITION_ADDRESS); return fCorrectSize && fKnownVersion; } @@ -292,6 +310,8 @@ CTxDestination CBitcoinAddress::Get() const return CKeyID(id); else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) return CScriptID(id); + else if (vchVersion == Params().Base58Prefix(CChainParams::CRYPTOCONDITION_ADDRESS)) + return CCryptoConditionID(id); else return CNoDestination(); } @@ -308,8 +328,11 @@ bool CBitcoinAddress::GetIndexKey(uint160& hashBytes, int& type, bool ccflag) co memcpy(&hashBytes, &vchData[0], 20); type = 2; return true; + } else if (vchVersion == Params().Base58Prefix(CChainParams::CRYPTOCONDITION_ADDRESS)) { + memcpy(&hashBytes, &vchData[0], 20); + type = 3; + return true; } - return false; } @@ -355,6 +378,22 @@ bool CCustomBitcoinAddress::Set(const CScriptID& id) return true; } +bool CCustomBitcoinAddress::Set(const CCryptoConditionID& id) +{ + SetData(base58Prefixes[0], &id, 20); // dimxy: CCryptoConditionID is actually CKeyID so we use base58Prefixes[0] as only two prefixes are supported in CCustomBitcoinAddress. + // TODO: check how it would work in gateways and importgateways + return true; +} + +bool CCustomBitcoinAddress::Set(const CCLTVID& id) +{ + if (id.which() == TX_PUBKEY) + Set(id.GetPubKey()); + else + Set(id.GetKeyID()); + return true; +} + bool CCustomBitcoinAddress::Set(const CTxDestination& dest) { return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); diff --git a/src/base58.h b/src/base58.h index 8be0247e08f..ee6d026f86b 100644 --- a/src/base58.h +++ b/src/base58.h @@ -133,6 +133,8 @@ class CBitcoinAddress : public CBase58Data { virtual bool Set(const CKeyID &id); virtual bool Set(const CPubKey &key); virtual bool Set(const CScriptID &id); + virtual bool Set(const CCryptoConditionID &id); + virtual bool Set(const CCLTVID& id); bool Set(const CTxDestination &dest); bool IsValid() const; bool IsValid(const CChainParams ¶ms) const; @@ -158,6 +160,8 @@ class CCustomBitcoinAddress : public CBitcoinAddress { bool Set(const CPubKey &key); bool Set(const CScriptID &id); bool Set(const CTxDestination &dest); + bool Set(const CCryptoConditionID &id); + bool Set(const CCLTVID& id); bool IsValid() const; CCustomBitcoinAddress() {} diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 544972586aa..c1229d53a4e 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -24,6 +24,7 @@ #include "rpc/protocol.h" #include "util.h" #include "utilstrencodings.h" +#include "komodo_globals.h" #include #include @@ -31,8 +32,6 @@ #include #include #include "support/events.h" -uint16_t BITCOIND_RPCPORT = 7771; -char ASSETCHAINS_SYMBOL[65]; #include @@ -95,18 +94,38 @@ static int AppInitRPC(int argc, char* argv[]) // Parameters // ParseParameters(argc, argv); + +#if defined(CUSTOM_CLIENT_ARGS) + // add custom chain parameters + try { + AddSettings(mapArgs, mapMultiArgs, CUSTOM_CLIENT_ARGS); + } catch (const std::exception& e) { + fprintf(stderr,"Error in custom parameters: %s\n", e.what()); + return EXIT_FAILURE; + } +#endif + + std::string cliname = "komodo-cli"; + std::string brandname = "Komodo"; +#if defined(CUSTOM_BIN_NAME) + cliname = std::string(CUSTOM_BIN_NAME) + "-cli"; +#endif +#if defined(CUSTOM_BRAND_NAME) + brandname = std::string("Komodo") + " " + std::string(CUSTOM_BRAND_NAME); +#endif + std:string name; name = GetArg("-ac_name",""); if ( !name.empty() ) strncpy(ASSETCHAINS_SYMBOL,name.c_str(),sizeof(ASSETCHAINS_SYMBOL)-1); if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) { - std::string strUsage = _("Komodo RPC client version") + " " + FormatFullVersion() + "\n" + PrivacyInfo(); + std::string strUsage = brandname + " " + _("RPC client version") + " " + FormatFullVersion() + "\n" + PrivacyInfo(); if (!mapArgs.count("-version")) { strUsage += "\n" + _("Usage:") + "\n" + - " komodo-cli [options] [params] " + _("Send command to Komodo") + "\n" + - " komodo-cli [options] help " + _("List commands") + "\n" + - " komodo-cli [options] help " + _("Get help for a command") + "\n"; + " " + cliname + " [options] [params] " + _("Send command to ") + brandname + "\n" + + " " + cliname + " [options] help " + _("List commands") + "\n" + + " " + cliname + " [options] help " + _("Get help for a command") + "\n"; strUsage += "\n" + HelpMessageCli(); } else { @@ -124,6 +143,7 @@ static int AppInitRPC(int argc, char* argv[]) fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str()); return EXIT_FAILURE; } + try { ReadConfigFile(mapArgs, mapMultiArgs); } catch (const std::exception& e) { diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index ef7b09a37f0..0e6492c3580 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -57,15 +57,6 @@ static bool fDaemon; #include "komodo_defs.h" -#define KOMODO_ASSETCHAIN_MAXLEN 65 -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -extern int32_t ASSETCHAINS_BLOCKTIME; -extern uint64_t ASSETCHAINS_CBOPRET; -void komodo_passport_iteration(); -uint64_t komodo_interestsum(); -int32_t komodo_longestchain(); -void komodo_cbopretupdate(int32_t forceflag); -CBlockIndex *komodo_chainactive(int32_t height); void WaitForShutdown(boost::thread_group* threadGroup) { @@ -77,14 +68,7 @@ void WaitForShutdown(boost::thread_group* threadGroup) fprintf(stderr,"error: earlytx must be before block height %d or tx does not exist\n",KOMODO_EARLYTXID_HEIGHT); StartShutdown(); } - /*if ( ASSETCHAINS_STAKED == 0 && ASSETCHAINS_ADAPTIVEPOW == 0 && (pindex= komodo_chainactive(1)) != 0 ) - { - if ( pindex->nTime > ADAPTIVEPOW_CHANGETO_DEFAULTON ) - { - ASSETCHAINS_ADAPTIVEPOW = 1; - fprintf(stderr,"default activate adaptivepow\n"); - } else fprintf(stderr,"height1 time %u vs %u\n",pindex->nTime,ADAPTIVEPOW_CHANGETO_DEFAULTON); - } //else fprintf(stderr,"cant find height 1\n");*/ + if ( ASSETCHAINS_CBOPRET != 0 ) komodo_pricesinit(); /* @@ -108,11 +92,6 @@ void WaitForShutdown(boost::thread_group* threadGroup) // // Start // -extern int32_t IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY; -extern uint32_t ASSETCHAIN_INIT; -extern std::string NOTARY_PUBKEY; -int32_t komodo_is_issuer(); -void komodo_passport_iteration(); bool AppInit(int argc, char* argv[]) { @@ -120,7 +99,16 @@ bool AppInit(int argc, char* argv[]) CScheduler scheduler; bool fRet = false; - + std::string daemonname = "komodod"; + std::string cliname = "komodo-cli"; +#if defined(CUSTOM_BIN_NAME) + daemonname = std::string(CUSTOM_BIN_NAME) + "d"; + cliname = std::string(CUSTOM_BIN_NAME) + "-cli"; +#endif + std::string brandname = "Komodo"; +#if defined(CUSTOM_BRAND_NAME) + brandname = std::string("Komodo") + " " + std::string(CUSTOM_BRAND_NAME); +#endif // // Parameters // @@ -139,7 +127,7 @@ bool AppInit(int argc, char* argv[]) else { strUsage += "\n" + _("Usage:") + "\n" + - " komodod [options] " + _("Start Komodo Daemon") + "\n"; + " " + daemonname + " [options] " + _("Start ") + brandname + _(" Daemon") + "\n"; strUsage += "\n" + HelpMessage(HMM_BITCOIND); } @@ -155,7 +143,14 @@ bool AppInit(int argc, char* argv[]) fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n"); return false; } + +#if defined(CUSTOM_SERVER_ARGS) + // add custom chain parameters + AddSettings(mapArgs, mapMultiArgs, CUSTOM_SERVER_ARGS); +#endif + void komodo_args(char *argv0); + komodo_args(argv[0]); void chainparams_commandline(); chainparams_commandline(); @@ -172,16 +167,16 @@ bool AppInit(int argc, char* argv[]) ReadConfigFile(mapArgs, mapMultiArgs); } catch (const missing_zcash_conf& e) { fprintf(stderr, - (_("Before starting komodod, you need to create a configuration file:\n" + (_("Before starting ") + daemonname + _(", 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" + "configuration of ") + daemonname + _(". But requiring a configuration file to start ensures\n" + "that ") + daemonname + _(" 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") + + "depending on how you installed ") + brandname + _(":\n") + _("- Source code: %s\n" "- .deb package: %s\n")).c_str(), GetConfigFile().string().c_str(), @@ -196,12 +191,12 @@ bool AppInit(int argc, char* argv[]) // Command-line RPC bool fCommandLine = false; for (int i = 1; i < argc; i++) - if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "komodo:")) + if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "komodo:")) // Are there any "komodo:" params? fCommandLine = true; if (fCommandLine) { - fprintf(stderr, "Error: There is no RPC client functionality in komodod. Use the komodo-cli utility instead.\n"); + std::cerr << "Error: There is no RPC client functionality in " << daemonname << ". Use the " << cliname << " utility instead." << std::endl; exit(EXIT_FAILURE); } @@ -209,7 +204,7 @@ bool AppInit(int argc, char* argv[]) fDaemon = GetBoolArg("-daemon", false); if (fDaemon) { - fprintf(stdout, "Komodo %s server starting\n",ASSETCHAINS_SYMBOL); + fprintf(stdout, "Komodo %s server starting\n", ASSETCHAINS_SYMBOL); // Daemonize pid_t pid = fork(); diff --git a/src/cc/CCImportGateway.h b/src/cc/CCImportGateway.h index ee741a1487e..a139538803e 100644 --- a/src/cc/CCImportGateway.h +++ b/src/cc/CCImportGateway.h @@ -33,4 +33,8 @@ UniValue ImportGatewayExternalAddress(uint256 bindtxid,CPubKey pubkey); UniValue ImportGatewayDumpPrivKey(uint256 bindtxid,CKey key); UniValue ImportGatewayList(); UniValue ImportGatewayInfo(uint256 bindtxid); + +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); +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); + #endif diff --git a/src/cc/CCNFTData.cpp b/src/cc/CCNFTData.cpp deleted file mode 100644 index af683cfd637..00000000000 --- a/src/cc/CCNFTData.cpp +++ /dev/null @@ -1,257 +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.h" -#include "CCNFTData.h" - -typedef std::pair nftPropDesc_t; -typedef std::map nftPropDesc_map; -static const nftPropDesc_map nftPropDesc = { - { NFTPROP_ID, { NFTTYP_UINT64, true }}, - { NFTPROP_URL, { NFTTYP_VUINT8, true }}, - { NFTPROP_ROYALTY, { NFTTYP_UINT64, false }}, - { NFTPROP_ARBITRARY, { NFTTYP_VUINT8, false }} -}; - -typedef struct { - nftPropType type; - struct - { - uint64_t uint64; - vuint8_t vuint8; - } v; -} nftPropValue; - -static bool ParseNftData(const vuint8_t &vdata, std::map &propMap, std::string &sError) -{ - if (vdata.size() > 2 && vdata[0] == EVAL_NFTDATA) - { - uint8_t evalCode, version; - bool invalidPropType = false; - bool hasDuplicates = false; - const char *funcname = __func__; - int mandatoryCount = 0; - - if (vdata[1] != NFTDATA_VERSION) - { - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "invalid nft data version" << std::endl); - sError = "invalide nft data version"; - return false; - } - propMap.clear(); - - if (E_UNMARSHAL(vdata, ss >> evalCode; ss >> version; // evalcode in the first byte - while(!ss.eof()) - { - uint8_t propId; - ss >> propId; - auto itDesc = nftPropDesc.find((nftPropId)propId); - nftPropValue value; - nftPropType type = itDesc != nftPropDesc.end() ? itDesc->second.first : NFTTYP_INVALID; - switch(type) { - case NFTTYP_UINT64: - value.type = type; - ss >> COMPACTSIZE(value.v.uint64); - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << funcname << " parsed prop id=" << (int)propId << " compactsize value=" << value.v.uint64 << std::endl); - break; - case NFTTYP_VUINT8: - value.type = type; - ss >> value.v.vuint8; - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << funcname << " parsed prop id=" << (int)propId << " varbuffer=" << HexStr(value.v.vuint8) << std::endl); - break; - default: - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << funcname << " property id not allowed (" << (int)propId << ")" << std::endl); - invalidPropType = true; - break; - } - if (invalidPropType) - break; - if (propMap.count((nftPropId)propId) != 0) - hasDuplicates = true; - else { - propMap.insert(std::make_pair((nftPropId)propId, value)); - if (itDesc->second.second) // if mandatory - mandatoryCount ++; - } - } - )) - { - if (invalidPropType) { - sError = "invalid property type"; - return false; - } - if (hasDuplicates) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << __func__ << " duplicates in nft data " << std::endl); - sError = "duplicate property type"; - return false; - } - if (mandatoryCount != std::count_if(nftPropDesc.begin(), nftPropDesc.end(), [](std::pair e){ return e.second.second; })) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << __func__ << " invalid mandatory property count=" << mandatoryCount << std::endl); - sError = "not all mandatory properties"; - return false; - } - return true; - } - else - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << __func__ << " could not unmarshal nft data" << std::endl); - } - sError = "could not unmarshal nft data"; - return false; -} - -bool GetNftDataAsUint64(const vuint8_t &vdata, nftPropId propId, uint64_t &val) -{ - std::map propMap; - std::string sError; - ParseNftData(vdata, propMap, sError); - if (propMap.count(propId) > 0 && propMap[propId].type == NFTTYP_UINT64) { - val = propMap[propId].v.uint64; - return true; - } - return false; -} - -bool GetNftDataAsVuint8(const vuint8_t &vdata, nftPropId propId, vuint8_t &val) -{ - std::map propMap; - std::string sError; - - ParseNftData(vdata, propMap, sError); - if (propMap.count(propId) > 0 && propMap[propId].type == NFTTYP_VUINT8) { - val = propMap[propId].v.vuint8; - return true; - } - return false; -} - -static bool ValidateNftData(const vuint8_t &vdata, std::string &sError) -{ - std::map propMap; - if( ParseNftData(vdata, propMap, sError) ) { - if (propMap[NFTPROP_ROYALTY].v.uint64 >= NFTROYALTY_DIVISOR ) { - sError = "invalid royalty value (must be in 0...999)"; - return false; - } - return true; - } - return false; -} - -bool ValidatePrevTxNFTOpretV1(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, const CScript &opret) -{ - uint256 tokenid; - std::vector vpks; - std::vector voprets; - if (DecodeTokenOpRetV1(opret, tokenid, vpks, voprets) == 0) - return eval->Error("could not decode nft data"); - - for(auto const &vin : tx.vin) - { - if (vin.prevout.hash == tokenid) // for token v1 check directly spent token create tx - { - CTransaction vintx; - uint256 hashBlock; - vuint8_t vorigpk; - std::string name, desc; - std::vector vcroprets; - std::string sError; - - if (!myGetTransaction(vin.prevout.hash, vintx, hashBlock)) - return eval->Error("could load vintx"); - if (DecodeTokenCreateOpRetV1(vintx.vout.back().scriptPubKey, vorigpk, name, desc, vcroprets) == 0) - return eval->Error("could not decode token create tx opreturn"); - if (vcroprets.size() == 0) - return eval->Error("no nft data in opreturn"); - if (!ValidateNftData(vcroprets[0], sError)) - return eval->Error("nft data invalid: " + sError); - } - } - return true; -} - -// check token v2 create tx -bool ValidateNFTOpretV2(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, const CScript &opret) -{ - vuint8_t vorigpk; - std::string name, desc; - std::vector vcroprets; - - if (IsTokenCreateFuncid(DecodeTokenCreateOpRetV2(opret, vorigpk, name, desc, vcroprets))) - { - std::string sError; - - if (vcroprets.size() == 0) - return eval->Error("no nft data in opreturn"); - if (!ValidateNftData(vcroprets[0], sError)) - return eval->Error("nft data invalid: " + sError); - } - return true; -} - -bool NFTDataValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) -{ - if (strcmp(ASSETCHAINS_SYMBOL, "TKLTEST") == 0 && chainActive.Height() <= 33711) - return true; - //if (strcmp(ASSETCHAINS_SYMBOL, "DIMXY20") == 0 && chainActive.Height() <= 1704) - // return true; - - if (tx.vout.size() < 1) - return eval->Error("no vouts"); - - vuint8_t vopret; - uint8_t evalTokens; - int goodTokenData = 0; - - // check if this is a token v2 create tx - if (GetOpReturnData(tx.vout.back().scriptPubKey, vopret) && - vopret.size() > 2 && - vopret[0] == EVAL_TOKENSV2) - { - if (IsTokenCreateFuncid(vopret[1])) - return ValidateNFTOpretV2(cp, eval, tx, tx.vout.back().scriptPubKey); - else - return true; // do not check further tokens txns - } - - // for token v1 we do not have create tx in validation - // let's find NFT vout and check if previous tx - for (int i = 0; i < tx.vout.size(); i ++) { - //uint256 tokenid; - //std::string errstr; - if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition()) - { - CScript ccdata; - vuint8_t vopdrop; - if (GetCCDropAsOpret(tx.vout[i].scriptPubKey, ccdata) && - GetOpReturnData(ccdata, vopdrop) && - vopdrop.size() > 2 && - vopdrop[0] == EVAL_TOKENS) - { - if( !ValidatePrevTxNFTOpretV1(cp, eval, tx, ccdata) ) - return false; // eval is set - } - } - } - - // if last vout v1 opreturn exists - if (vopret.size() > 2 && - vopret[0] == EVAL_TOKENS) - { - if (!ValidatePrevTxNFTOpretV1(cp, eval, tx, tx.vout.back().scriptPubKey)) - return false; - } - return true; // no prev tx is token create tx -} - diff --git a/src/cc/CCNFTData.h b/src/cc/CCNFTData.h deleted file mode 100644 index a77fff4f07e..00000000000 --- a/src/cc/CCNFTData.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef CC_NFTDATA_H -#define CC_NFTDATA_H - -#include "CCinclude.h" - -const uint32_t NFTDATA_VERSION = 1; - -const uint64_t NFTROYALTY_DIVISOR = 1000; - -// nft Prop id: -enum nftPropId : uint8_t { - NFTPROP_NONE = 0x0, - NFTPROP_ID = 0x1, - NFTPROP_URL = 0x2, - NFTPROP_ROYALTY = 0x3, - NFTPROP_ARBITRARY = 0x4 -}; - -// nft property type -enum nftPropType : uint8_t { - NFTTYP_INVALID = 0x0, - NFTTYP_UINT64 = 0x1, - NFTTYP_VUINT8 = 0x2 -}; - -bool GetNftDataAsUint64(const vuint8_t &vdata, nftPropId propId, uint64_t &val); -bool GetNftDataAsVuint8(const vuint8_t &vdata, nftPropId propId, vuint8_t &val); - -bool NFTDataValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn); - -#endif diff --git a/src/cc/CCTokelData.cpp b/src/cc/CCTokelData.cpp new file mode 100644 index 00000000000..063bb1179db --- /dev/null +++ b/src/cc/CCTokelData.cpp @@ -0,0 +1,341 @@ +/****************************************************************************** +* Copyright 2014-2021 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 "CCtokens.h" +#include "CCTokelData.h" + +static UniValue tklReadString(CDataStream &ss) +{ + std::string sval; + ::Unserialize(ss, sval); + UniValue ret(sval); + return ret; +} + +static UniValue tklReadInt64(CDataStream &ss) +{ + uint64_t ui64val; + //::Unserialize(ss, i64val); + ss >> COMPACTSIZE(ui64val); + UniValue ret((int64_t)ui64val); + return ret; +} + +static UniValue tklReadVuint8(CDataStream &ss) +{ + vuint8_t vuint8val; + ::Unserialize(ss, vuint8val); + UniValue ret(HexStr(vuint8val)); + return ret; +} + +static bool tklWriteString(CDataStream &ss, const UniValue &val, std::string &err) +{ + std::string sval = val.getValStr(); + ::Serialize(ss, sval); + return true; +} + +static bool tklWriteInt64(CDataStream &ss, const UniValue &val, std::string &err) +{ + int64_t i64val = 0; + if (!val.isNum()) + return false; + ParseInt64(val.getValStr(), &i64val); + if (i64val > MAX_SIZE) { + err = "value too big"; + return false; + } + ss << COMPACTSIZE((uint64_t)i64val); + return true; +} + +static bool tklWriteVuint8(CDataStream &ss, const UniValue &val, std::string &err) +{ + std::string s = val.getValStr(); + for(auto const &c : s) + if (!std::isxdigit(c)) { + err = "not hex string"; + return false; + } + vuint8_t vuint8val = ParseHex(s); + ::Serialize(ss, vuint8val); + return true; +} + +typedef std::map tklPropDesc_map; +static const tklPropDesc_map tklPropDesc = { + { TKLPROP_ID, std::make_tuple(TKLTYP_INT64, std::string("id"), &tklReadInt64, &tklWriteInt64) }, + { TKLPROP_URL, std::make_tuple(TKLTYP_VUINT8, std::string("url"), &tklReadString, &tklWriteString) }, + { TKLPROP_ROYALTY, std::make_tuple(TKLTYP_INT64, std::string("royalty"), &tklReadInt64, &tklWriteInt64) }, + { TKLPROP_ARBITRARY, std::make_tuple(TKLTYP_VUINT8, std::string("arbitrary"), &tklReadVuint8, &tklWriteVuint8) } +}; + +static tklPropId FindTokelDataIdByName(const std::string &name) +{ + auto found = std::find_if(tklPropDesc.begin(), tklPropDesc.end(), [&](const std::pair &e){ return std::get<1>(e.second) == name; }); + if (found != tklPropDesc.end()) + return found->first; + else + return (tklPropId)0; +} + +static tklPropDesc_t GetTokelDataDesc(tklPropId id) +{ + static const tklPropDesc_t empty = std::make_tuple( TKLTYP_INVALID, std::string(), nullptr, nullptr ); + auto found = tklPropDesc.find(id); + if (found != tklPropDesc.end()) + return found->second; + else + return empty; +} + +vuint8_t ParseTokelJson(const UniValue &jsonParams) +{ + uint8_t evalcode = EVAL_TOKELDATA; + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << evalcode << (uint8_t)TKLDATA_VERSION; + for(int i = 0; i < jsonParams.getKeys().size(); i ++) + { + std::string key = jsonParams.getKeys()[i]; + tklPropId id; + if ((id = FindTokelDataIdByName(key)) == (tklPropId)0) + throw std::runtime_error("invalid token data id: '" + key + "'"); + tklPropDesc_t entry = GetTokelDataDesc(id); + ss << (uint8_t)id; + std::string err; + if (!(*std::get<3>(entry))(ss, jsonParams[key], err)) + throw std::runtime_error(std::string("tokel data invalid: '") + key + "' " + err); + } + + return vuint8_t(ss.begin(), ss.end()); +} + +static bool UnmarshalTokelVData(const vuint8_t &vdata, std::map &propMapOut, std::string &sError) +{ + if (vdata.size() >= 2 && vdata[0] == EVAL_TOKELDATA) + { + uint8_t evalCode, version; + bool invalidPropType = false; + bool hasDuplicates = false; + const char *funcname = __func__; + std::map propMap; + + if (vdata[1] != TKLDATA_VERSION) + { + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "invalid tkl data version" << std::endl); + sError = "invalid tkl data version"; + return false; + } + + try { + CDataStream ss(vdata, SER_NETWORK, PROTOCOL_VERSION); + ::Unserialize(ss, evalCode); + ::Unserialize(ss, version); + while(!ss.eof()) { + uint8_t id; + ::Unserialize(ss, id); + tklPropDesc_t entry = GetTokelDataDesc((tklPropId)id); + if (std::get<0>(entry) == TKLTYP_INVALID) { + sError = "invalid tokel data property type"; + return false; + } + if (propMap.count((tklPropId)id) != 0) { + sError = "duplicate tokel data property type"; + return false; + } + UniValue val = (*std::get<2>(entry))(ss); // read ss + propMap.insert(std::make_pair((tklPropId)id, val)); + } + propMapOut = propMap; + return true; + } catch(std::system_error se) { + sError = std::string("could not parse tokel token data: ") + se.what(); + return false; + } catch(...) { + sError = std::string("could not parse tokel token data"); + return false; + } + } + sError = "invalid tokel data"; + return false; +} + +UniValue ParseTokelVData(const vuint8_t &vdata) +{ + std::map propMap; + std::string sError; + UniValue result(UniValue::VOBJ); + + UnmarshalTokelVData(vdata, propMap, sError); + int i = 0; + for (auto const &p : propMap) { + tklPropDesc_t entry = GetTokelDataDesc(p.first); + if (std::get<0>(entry) != TKLTYP_INVALID) + result.pushKV(std::get<1>(entry), p.second); + else + result.pushKV(std::string("unknown")+std::to_string(++i), p.second); + } + return result; +} + +bool GetTokelDataAsInt64(const vuint8_t &vdata, tklPropId propId, int64_t &val) +{ + std::map propMap; + std::string sError; + UnmarshalTokelVData(vdata, propMap, sError); + if (propMap.count(propId) > 0 && propMap[propId].isNum()) { + ParseInt64(propMap[propId].getValStr(), &val); + return true; + } + return false; +} + +bool GetTokelDataAsVuint8(const vuint8_t &vdata, tklPropId propId, vuint8_t &val) +{ + std::map propMap; + std::string sError; + + UnmarshalTokelVData(vdata, propMap, sError); + if (propMap.count(propId) > 0) { + val = ParseHex(propMap[propId].getValStr()); + return true; + } + return false; +} + +bool CheckTokelData(const vuint8_t &vdata, std::string &sError) +{ + std::map propMap; + if (UnmarshalTokelVData(vdata, propMap, sError)) { + // check props if present + if (propMap.count(TKLPROP_ROYALTY) > 0) { + int64_t val; + if (!ParseInt64(propMap[TKLPROP_ROYALTY].getValStr(), &val)) { + sError = "could not parse tokel royalty"; + return false; + } + if (val < 0 || val >= TKLROYALTY_DIVISOR) { + sError = "invalid tokel royalty value (must be in 0...999)"; + return false; + } + } + return true; + } + return false; +} + +bool ValidatePrevTxTokelOpretV1(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, const CScript &opret) +{ + uint256 tokenid; + std::vector vpks; + std::vector voprets; + if (DecodeTokenOpRetV1(opret, tokenid, vpks, voprets) == 0) + return eval->Error("could not decode tkl data"); + + for(auto const &vin : tx.vin) + { + if (vin.prevout.hash == tokenid) // for token v1 check directly spent token create tx + { + CTransaction vintx; + uint256 hashBlock; + vuint8_t vorigpk; + std::string name, desc; + std::vector vcroprets; + std::string sError; + + if (!eval->GetTxUnconfirmed(vin.prevout.hash, vintx, hashBlock)) + return eval->Error("could load vintx"); + if (DecodeTokenCreateOpRetV1(vintx.vout.back().scriptPubKey, vorigpk, name, desc, vcroprets) == 0) + return eval->Error("could not decode token create tx opreturn"); + if (vcroprets.size() == 0) + return eval->Error("no tkl data in opreturn"); + if (!CheckTokelData(vcroprets[0], sError)) + return eval->Error("tkl data invalid: " + sError); + } + } + return true; +} + +// check token v2 create tx +bool ValidateTokelOpretV2(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, const CScript &opret) +{ + vuint8_t vorigpk; + std::string name, desc; + std::vector vcroprets; + + if (IsTokenCreateFuncid(DecodeTokenCreateOpRetV2(opret, vorigpk, name, desc, vcroprets))) + { + std::string sError; + + if (vcroprets.size() == 0) + return eval->Error("no tkl data in opreturn"); + if (!CheckTokelData(vcroprets[0], sError)) + return eval->Error("tkl data invalid: " + sError); + } + return true; +} + +bool TokelDataValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) +{ + if (tx.vout.size() < 1) + return eval->Error("no vouts"); + + vuint8_t vopret; + uint8_t evalTokens; + int goodTokenData = 0; + + // check if this is a token v2 create tx + if (GetOpReturnData(tx.vout.back().scriptPubKey, vopret) && + vopret.size() > 2 && + vopret[0] == EVAL_TOKENSV2) + { + if (IsTokenCreateFuncid(vopret[1])) + return ValidateTokelOpretV2(cp, eval, tx, tx.vout.back().scriptPubKey); + else + return true; // do not check further tokens txns + } + + // for token v1 we do not have create tx in validation + // let's find TKL vout and check if previous tx + for (int i = 0; i < tx.vout.size(); i ++) { + //uint256 tokenid; + //std::string errstr; + if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition()) + { + CScript ccdata; + vuint8_t vopdrop; + if (!(ccdata = GetCCDropAsOpret(tx.vout[i].scriptPubKey)).empty() && + GetOpReturnData(ccdata, vopdrop) && + vopdrop.size() > 2 && + vopdrop[0] == EVAL_TOKENS) + { + if( !ValidatePrevTxTokelOpretV1(cp, eval, tx, ccdata) ) + return false; // eval is set + } + } + } + + // if last vout v1 opreturn exists + if (vopret.size() > 2 && + vopret[0] == EVAL_TOKENS) + { + if (!ValidatePrevTxTokelOpretV1(cp, eval, tx, tx.vout.back().scriptPubKey)) + return false; + } + return true; // no prev tx is token create tx +} + diff --git a/src/cc/CCTokelData.h b/src/cc/CCTokelData.h new file mode 100644 index 00000000000..b1b5b8b13fb --- /dev/null +++ b/src/cc/CCTokelData.h @@ -0,0 +1,42 @@ +#ifndef CC_TKLDATA_H +#define CC_TKLDATA_H + +#include "CCinclude.h" + +const uint8_t TKLDATA_NULL_EVAL = 0; +const uint8_t TKLDATA_VERSION = 1; +const uint64_t TKLROYALTY_DIVISOR = 1000; + +// tkl Prop id: +enum tklPropId : uint8_t { + TKLPROP_NONE = 0x0, + TKLPROP_ID = 0x1, + TKLPROP_URL = 0x2, + TKLPROP_ROYALTY = 0x3, + TKLPROP_ARBITRARY = 0x4 +}; + +// tkl property type +enum tklPropType : uint8_t { + TKLTYP_INVALID = 0x0, + TKLTYP_INT64 = 0x1, + TKLTYP_VUINT8 = 0x2 +}; + +typedef bool tklWriteSS(CDataStream &ss, const UniValue &val, std::string &err); +typedef UniValue tklReadSS(CDataStream &ss); + + + +typedef std::tuple tklPropDesc_t; + +bool GetTokelDataAsInt64(const vuint8_t &vdata, tklPropId propId, int64_t &val); +bool GetTokelDataAsVuint8(const vuint8_t &vdata, tklPropId propId, vuint8_t &val); +vuint8_t ParseTokelJson(const UniValue &jsonParams); +UniValue ParseTokelVData(const vuint8_t &vdata); +bool CheckTokelData(const vuint8_t &vdata, std::string &sError); + + +bool TokelDataValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn); + +#endif diff --git a/src/cc/CCassets.h b/src/cc/CCassets.h index 92a00829755..cd58bbd3564 100644 --- a/src/cc/CCassets.h +++ b/src/cc/CCassets.h @@ -25,57 +25,30 @@ #include "CCinclude.h" -#include "old/CCassets_v0.h" - #define ASSETS_GLOBALADDR_VIN 1 #define ASSETS_GLOBALADDR_VOUT 0 #define ASSETS_MARKER_AMOUNT 10000 -#define ASSETS_NORMAL_DUST 500 // CCcustom bool AssetsValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn); bool Assetsv2Validate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn); // CCassetsCore -vscript_t EncodeAssetOpRetV1(uint8_t assetFuncId, uint256 assetid2, int64_t unit_price, std::vector origpubkey); -uint8_t DecodeAssetTokenOpRetV1(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &unit_price, std::vector &origpubkey); -vscript_t EncodeAssetOpRetV2(uint8_t assetFuncId, uint256 assetid2, int64_t unit_price, std::vector origpubkey); -uint8_t DecodeAssetTokenOpRetV2(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); - -// 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); - -//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); +vscript_t EncodeAssetOpRetV1(uint8_t assetFuncId, CAmount unit_price, vscript_t origpubkey, int32_t expiryHeight); +uint8_t DecodeAssetTokenOpRetV1(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, CAmount &unit_price, vscript_t &origpubkey, int32_t &expiryHeight); +vscript_t EncodeAssetOpRetV2(uint8_t assetFuncId, CAmount unit_price, vscript_t origpubkey, int32_t expiryHeight); +uint8_t DecodeAssetTokenOpRetV2(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, CAmount &unit_price, vscript_t &origpubkey, int32_t &expiryHeight); + +bool ValidateBidRemainder(CAmount unit_price, CAmount remaining_nValue, CAmount orig_nValue, CAmount received_nValue, CAmount paid_units); +bool ValidateAskRemainder(CAmount unit_price, CAmount remaining_assetoshis, CAmount orig_assetoshis, CAmount received_assetoshis, CAmount paid_nValue); +bool ValidateSwapRemainder(CAmount remaining_units, CAmount remaining_nValue, CAmount orig_nValue, CAmount received_nValue, CAmount paidprice, CAmount totalprice); +bool SetBidFillamounts(CAmount unit_price, CAmount &received_nValue, CAmount orig_nValue, CAmount &paid_units, CAmount orig_units, CAmount paid_unit_price); +bool SetAskFillamounts(CAmount unit_price, CAmount fill_assetoshis, CAmount orig_assetoshis, CAmount paid_nValue); +bool SetSwapFillamounts(CAmount unit_price, CAmount &paid, CAmount orig_nValue, CAmount &received, CAmount totalprice); // not implemented +CAmount AssetsGetTxCCInputs(Eval *eval, struct CCcontract_info *cp, const char *addr, const CTransaction &tx); +CAmount AssetsGetTxTokenInputs(Eval *eval, struct CCcontract_info *cpTokens, const CTransaction &tx); +bool AssetsFillOrderIsDust(int32_t royaltyFract, CAmount nOutputValue, bool &isRoyaltyDust); + const char ccassets_log[] = "ccassets"; @@ -86,17 +59,17 @@ class AssetsV1 { static uint8_t TokensEvalCode() { return EVAL_TOKENS; } static bool IsMixed() { return false; } - static vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t unit_price, std::vector origpubkey) + static vscript_t EncodeAssetOpRet(uint8_t assetFuncId, CAmount unit_price, vscript_t origpubkey, int32_t expiryHeight) { - return ::EncodeAssetOpRetV1(assetFuncId, assetid2, unit_price, origpubkey); + return ::EncodeAssetOpRetV1(assetFuncId, unit_price, origpubkey, expiryHeight); } - static uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &unit_price, std::vector &origpubkey) + static uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, CAmount &unit_price, vscript_t &origpubkey, int32_t &expiryHeight) { - return ::DecodeAssetTokenOpRetV1(scriptPubKey, assetsEvalCode, tokenid, assetid2, unit_price, origpubkey); + return ::DecodeAssetTokenOpRetV1(scriptPubKey, assetsEvalCode, tokenid, unit_price, origpubkey, expiryHeight); } - static bool ConstrainVout(CTxOut vout, int32_t CCflag, char *cmpaddr, int64_t nValue, uint8_t evalCode) + static bool ConstrainVout(CTxOut vout, int32_t CCflag, char *cmpaddr, CAmount nValue, uint8_t evalCode) { return ::ConstrainVout(vout, CCflag, cmpaddr, nValue); } @@ -110,17 +83,17 @@ class AssetsV2 { static bool IsMixed() { return true; } - static vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t unit_price, std::vector origpubkey) + static vscript_t EncodeAssetOpRet(uint8_t assetFuncId, CAmount unit_price, vscript_t origpubkey, int32_t expiryHeight) { - return ::EncodeAssetOpRetV2(assetFuncId, assetid2, unit_price, origpubkey); + return ::EncodeAssetOpRetV2(assetFuncId, unit_price, origpubkey, expiryHeight); } - static uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &unit_price, std::vector &origpubkey) + static uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, CAmount &unit_price, vscript_t &origpubkey, int32_t &expiryHeight) { - return ::DecodeAssetTokenOpRetV2(scriptPubKey, assetsEvalCode, tokenid, assetid2, unit_price, origpubkey); + return ::DecodeAssetTokenOpRetV2(scriptPubKey, assetsEvalCode, tokenid, unit_price, origpubkey, expiryHeight); } - static bool ConstrainVout(CTxOut vout, int32_t CCflag, char *cmpaddr, int64_t nValue, uint8_t evalCode) + static bool ConstrainVout(CTxOut vout, int32_t CCflag, char *cmpaddr, CAmount nValue, uint8_t evalCode) { return ::ConstrainVoutV2(vout, CCflag, cmpaddr, nValue, evalCode); } diff --git a/src/cc/CCassetsCore_impl.h b/src/cc/CCassetsCore_impl.h index 9f1295fb509..d84fc786cff 100644 --- a/src/cc/CCassetsCore_impl.h +++ b/src/cc/CCassetsCore_impl.h @@ -18,7 +18,7 @@ #include "CCassets.h" #include "CCtokens.h" - +#include "CCupgrades.h" /* The SetAssetFillamounts() and ValidateAssetRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively. @@ -45,15 +45,16 @@ // validates opret for asset tx: template -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, CAmount &remaining_units_out, std::vector &origpubkey_out) { - uint256 assetidOpret, assetidOpret2; + uint256 assetidOpret; uint8_t funcid, evalCode; + int32_t expiryHeight; // this is just for log messages indentation fur debugging recursive calls: int32_t n = tx.vout.size(); - if ((funcid = A::DecodeAssetTokenOpRet(tx.vout.back().scriptPubKey, evalCode, assetidOpret, assetidOpret2, remaining_units_out, origpubkey_out)) == 0) + if ((funcid = A::DecodeAssetTokenOpRet(tx.vout.back().scriptPubKey, evalCode, assetidOpret, remaining_units_out, origpubkey_out, expiryHeight)) == 0) { LOGSTREAMFN(ccassets_log, CCLOG_DEBUG1, stream << "called DecodeAssetTokenOpRet returned funcId=0 for opret from txid=" << tx.GetHash().GetHex() << std::endl); return(false); @@ -66,19 +67,19 @@ bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &re return(true); } } - else if (funcid == 'E') // NOTE: not implemented yet! + /*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); - } + }*/ return false; } // Checks if the vout is a really Asset CC vout template -int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &remaining_units_out, std::vector &origpubkey_out, const CTransaction& tx, int32_t v, uint256 refassetid) +CAmount IsAssetvout(struct CCcontract_info *cp, CAmount &remaining_units_out, std::vector &origpubkey_out, const CTransaction& tx, int32_t v, uint256 refassetid) { // just check boundaries: int32_t n = tx.vout.size(); @@ -100,12 +101,11 @@ int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &remaining_units_out, st // extract sell/buy owner's pubkey from the opret template -uint8_t SetAssetOrigpubkey(std::vector &origpubkey_out, CAmount &unit_price, const CTransaction &tx) +uint8_t GetOrderParams(std::vector &origpubkey_out, CAmount &unit_price, uint256 &assetid, int32_t &expiryHeightOut, const CTransaction &tx) { - uint256 assetid, assetid2; uint8_t evalCode, funcid; - if (tx.vout.size() > 0 && (funcid = A::DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, unit_price, origpubkey_out)) != 0) + if (tx.vout.size() > 0 && (funcid = A::DecodeAssetTokenOpRet(tx.vout.back().scriptPubKey, evalCode, assetid, unit_price, origpubkey_out, expiryHeightOut)) != 0) return funcid; else return 0; @@ -115,16 +115,17 @@ uint8_t SetAssetOrigpubkey(std::vector &origpubkey_out, CAmount &unit_p template bool GetAssetorigaddrs(struct CCcontract_info *cp, char *origCCaddr, char *origNormalAddr, const CTransaction& vintx) { - uint256 assetid, assetid2; - int64_t price,nValue=0; + uint256 assetid; + CAmount price, nValue=0; int32_t n; uint8_t vintxFuncId; std::vector origpubkey; CScript script; uint8_t evalCode; + int32_t expiryHeight; n = vintx.vout.size(); - if( n == 0 || (vintxFuncId = A::DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 ) { + if( n == 0 || (vintxFuncId = A::DecodeAssetTokenOpRet(vintx.vout.back().scriptPubKey, evalCode, assetid, price, origpubkey, expiryHeight)) == 0 ) { LOGSTREAMFN(ccassets_log, CCLOG_INFO, stream << "could not get vintx opreturn" << std::endl); return(false); } @@ -133,7 +134,6 @@ bool GetAssetorigaddrs(struct CCcontract_info *cp, char *origCCaddr, char *origN cpTokens = CCinit(&tokensC, A::TokensEvalCode()); 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), A::IsMixed())) // tokens to single-eval token or token+nonfungible return false; } @@ -148,16 +148,17 @@ bool GetAssetorigaddrs(struct CCcontract_info *cp, char *origCCaddr, char *origN } template -int64_t AssetValidateCCvin(struct CCcontract_info *cpAssets, Eval* eval, char *origCCaddr_out, char *origaddr_out, const CTransaction &tx, int32_t vini, CTransaction &vinTx) +CAmount AssetValidateCCvin(struct CCcontract_info *cpAssets, Eval* eval, char *origCCaddr_out, char *origaddr_out, int32_t &vinExpiryHeightOut, const CTransaction &tx, int32_t vini, CTransaction &vinTx) { uint256 hashBlock; - uint256 assetid, assetid2; - uint256 vinAssetId, vinAssetId2; - int64_t tmpprice, vinPrice; + uint256 assetid; + uint256 vinAssetId; + CAmount tmpprice, vinPrice; std::vector tmporigpubkey; std::vector vinOrigpubkey; uint8_t evalCode; uint8_t vinEvalCode; + int32_t tmpExpiryHeight; char destaddr[KOMODO_ADDRESS_BUFSIZE], unspendableAddr[KOMODO_ADDRESS_BUFSIZE]; origaddr_out[0] = destaddr[0] = origCCaddr_out[0] = 0; @@ -165,21 +166,23 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cpAssets, Eval* eval, char *o uint8_t funcid = 0; uint8_t vinFuncId = 0; if (tx.vout.size() > 0) - funcid = A::DecodeAssetTokenOpRet(tx.vout.back().scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey); + funcid = A::DecodeAssetTokenOpRet(tx.vout.back().scriptPubKey, evalCode, assetid, tmpprice, tmporigpubkey, tmpExpiryHeight); else - return eval->Invalid("no vouts in tx"); - - 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 - return eval->Invalid("vin1 needs to be buyvin.vout[0]"); + return eval->Invalid("no vouts in tx"), 0LL; + if (funcid == 0) + return eval->Invalid("could not decode opreturn for tx"), 0LL; + + if (tx.vin.size() < 2) + return eval->Invalid("not enough vins"), 0LL; + else if(tx.vin[vini].prevout.n != ASSETS_GLOBALADDR_VOUT) // check global addr vout number == 0 + return eval->Invalid("asset cc vin must refer bid vintx.vout[0]"), 0LL; else if(eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash, vinTx, hashBlock) == false) { 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"); + return eval->Invalid("could not load previous tx or it has too few vouts"), 0LL; } - else if (vinTx.vout.size() < 1 || (vinFuncId = A::DecodeAssetTokenOpRet(vinTx.vout.back().scriptPubKey, vinEvalCode, vinAssetId, vinAssetId2, vinPrice, vinOrigpubkey)) == 0) { - return eval->Invalid("could not find assets opreturn in previous tx"); + else if (vinTx.vout.size() < 1 || (vinFuncId = A::DecodeAssetTokenOpRet(vinTx.vout.back().scriptPubKey, vinEvalCode, vinAssetId, vinPrice, vinOrigpubkey, vinExpiryHeightOut)) == 0) { + return eval->Invalid("could not find assets opreturn in previous tx"), 0LL; } // if fillSell or cancelSell --> should spend tokens from dual-eval token-assets global addr: else if((funcid == 'S' || funcid == 'x') && @@ -189,7 +192,7 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cpAssets, Eval* eval, char *o 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); - return eval->Invalid("invalid vin assets CCaddr"); + return eval->Invalid("invalid vin assets CCaddr"), 0LL; } // if fillBuy or cancelBuy --> should spend coins from asset cc global addr else if ((funcid == 'B' || funcid == 'o') && @@ -199,98 +202,112 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cpAssets, Eval* eval, char *o 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); - return eval->Invalid("invalid vin assets CCaddr"); + return eval->Invalid("invalid vin assets CCaddr"), 0LL; } // 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) - return eval->Invalid("couldnt get origaddr for vin tx"); + return eval->Invalid("couldnt get origaddr for vin tx"), 0LL; //fprintf(stderr,"AssetValidateCCvin() got %.8f to origaddr.(%s)\n", (double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr); // 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"); + if (std::find_if(vinTx.vin.begin(), vinTx.vin.end(), [&](const CTxIn &vin) { return cpAssets->ismyvin(vin.scriptSig); }) == vinTx.vin.end()) + return eval->Invalid("no assets cc vins in previous fillbuy or fillsell tx"), 0LL; } - // 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"); + // check no more other vins spending from global addr (must be only vintx.vout[0]): + if (AssetsGetTxCCInputs(eval, cpAssets, unspendableAddr, tx) != vinTx.vout[ASSETS_GLOBALADDR_VOUT].nValue) // gets either cc coins from global or tokens from dual eval global address + return eval->Invalid("invalid assets cc vins found"), 0LL; if (vinTx.vout[ASSETS_GLOBALADDR_VOUT].nValue == 0) - return eval->Invalid("null value in previous tx CC vout0"); + return eval->Invalid("null value in previous tx CC vout0"), 0LL; - return(vinTx.vout[ASSETS_GLOBALADDR_VOUT].nValue); + return vinTx.vout[ASSETS_GLOBALADDR_VOUT].nValue; } template -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) +CAmount AssetValidateBuyvin(struct CCcontract_info *cpAssets, Eval* eval, CAmount &unit_price, std::vector &origpubkey_out, char *origCCaddr_out, char *origaddr_out, int32_t &expiryHeightOut, const CTransaction &tx, uint256 refassetid) { - CTransaction vinTx; int64_t nValue; uint256 assetid, assetid2; uint8_t funcid, evalCode; + CTransaction vinTx; + CAmount nValue; + uint256 assetid; + uint8_t funcid, evalCode; + int32_t expiryHeight; origCCaddr_out[0] = origaddr_out[0] = 0; // get first ccvin: - int32_t ivincc; - for (ivincc = 0; ivincc < tx.vin.size(); ivincc ++) - if (cpAssets->ismyvin(tx.vin[ivincc].scriptSig)) - break; + auto ccvin = std::find_if(tx.vin.begin(), tx.vin.end(), [&](const CTxIn &vin) { return cpAssets->ismyvin(vin.scriptSig); }); + if (ccvin == tx.vin.end()) + return eval->Invalid("cc vin not found in tx"), 0LL; // validate locked coins on Assets vin[1] - if ((nValue = AssetValidateCCvin(cpAssets, eval, origCCaddr_out, origaddr_out, tx, /*ASSETS_GLOBALADDR_VIN*/ivincc, vinTx)) == 0) - return(0); + if ((nValue = AssetValidateCCvin(cpAssets, eval, origCCaddr_out, origaddr_out, expiryHeightOut, tx, ccvin-tx.vin.begin(), vinTx)) == 0) + return 0LL; // eval is set already in AssetValidateCCvin else if (vinTx.vout.size() < 2) - return eval->Invalid("invalid previous tx, too few vouts"); + return eval->Invalid("invalid previous tx, too few vouts"), 0LL; else if (vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == false) - return eval->Invalid("invalid not cc vout0 for buyvin"); - else if ((funcid = A::DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, unit_price, origpubkey_out)) == 'b' && + return eval->Invalid("invalid not cc vout0 for buyvin"), 0LL; + else if ((funcid = A::DecodeAssetTokenOpRet(vinTx.vout.back().scriptPubKey, evalCode, assetid, unit_price, origpubkey_out, expiryHeightOut)) == 'b' && vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == false) // marker is only in 'b'? - return eval->Invalid("invalid not cc vout1 for buyvin"); + return eval->Invalid("invalid not cc vout1 for buyvin"), 0LL; 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"); + if (funcid != 'b' && funcid != 'B') + return eval->Invalid("invalid opreturn funcid for buyvin"), 0LL; 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 eval->Invalid("invalid assetid for buyvin"), 0LL; } - return(nValue); + return nValue; } template -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) +CAmount AssetValidateSellvin(struct CCcontract_info *cpAssets, Eval* eval, CAmount &unit_price, std::vector &origpubkey_out, char *origCCaddr_out, char *origaddr_out, int32_t &expiryHeightOut, const CTransaction &tx, uint256 assetid) { - CTransaction vinTx; int64_t nValue, assetoshis; + CTransaction vinTxOut; + CAmount nValue, assetoshis; - //fprintf(stderr,"AssetValidateSellvin()\n"); // get first ccvin: - int32_t ivincc; - for (ivincc = 0; ivincc < tx.vin.size(); ivincc ++) - if (cpAssets->ismyvin(tx.vin[ivincc].scriptSig)) - break; - - if ((nValue = AssetValidateCCvin(cpAssets, eval, origCCaddr_out, origaddr_out, tx, /*ASSETS_GLOBALADDR_VIN*/ivincc, vinTx)) == 0) - return(0); + auto ccvin = std::find_if(tx.vin.begin(), tx.vin.end(), [&](const CTxIn &vin) { return cpAssets->ismyvin(vin.scriptSig); }); + if (ccvin == tx.vin.end()) + return eval->Invalid("cc vin not found"), 0LL; + if ((nValue = AssetValidateCCvin(cpAssets, eval, origCCaddr_out, origaddr_out, expiryHeightOut, tx, ccvin-tx.vin.begin(), vinTxOut)) == 0) + return 0LL; // eval->Invalid is set already in AssetValidateCCvin - if ((assetoshis = IsAssetvout(cpAssets, unit_price, origpubkey_out, vinTx, ASSETS_GLOBALADDR_VOUT, assetid)) == 0) - return eval->Invalid("invalid missing CC vout0 for sellvin"); + if ((assetoshis = IsAssetvout(cpAssets, unit_price, origpubkey_out, vinTxOut, ASSETS_GLOBALADDR_VOUT, assetid)) == 0) + return eval->Invalid("invalid missing CC vout0 for sellvin"), 0LL; else - return(assetoshis); + return assetoshis; } +template +bool AssetsValidateTokenId(Eval *eval, struct CCcontract_info *cp, const CTransaction &tx, int32_t v, uint256 assetid) +{ + CScript tokenOpret; + uint256 reftokenid; + uint8_t funcId; + std::string errorStr; + if (T::CheckTokensvout(cp, eval, tx, v, tokenOpret, reftokenid, funcId, errorStr) >= 0) { + if (reftokenid != assetid) + return eval->Invalid("invalid tokenid for tokenask"); + else + return true; + } + return eval->Invalid("invalid token tx"); +} +template +bool AssetsValidateTokenId_Activated(Eval *eval, struct CCcontract_info *cpTokens, const CTransaction &tx, int32_t v, uint256 assetid) +{ + if (CCUpgrades::IsUpgradeActive(eval->GetCurrentTime(), eval->GetCurrentHeight(), CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_ASSETS_OPDROP_VALIDATE_FIX)) + return AssetsValidateTokenId(eval, cpTokens, tx, v, assetid); + else + return true; +} #endif // #ifndef CC_ASSETS_CORE_IMPL_H diff --git a/src/cc/CCassetsUtils.cpp b/src/cc/CCassetsUtils.cpp index baa67e44e21..53c1d2b01df 100644 --- a/src/cc/CCassetsUtils.cpp +++ b/src/cc/CCassetsUtils.cpp @@ -15,9 +15,12 @@ #include "CCassets.h" #include "CCtokens.h" +#include "cc/CCupgrades.h" +#include "CCTokelData.h" + #include -vscript_t EncodeAssetOpRetV1(uint8_t assetFuncId, uint256 assetid2, int64_t unit_price, std::vector origpubkey) +vscript_t EncodeAssetOpRetV1(uint8_t assetFuncId, CAmount unit_price, vscript_t origpubkey, int32_t expiryHeight) { vscript_t vopret; uint8_t evalcode = EVAL_ASSETS; @@ -30,12 +33,12 @@ vscript_t EncodeAssetOpRetV1(uint8_t assetFuncId, uint256 assetid2, int64_t unit vopret = E_MARSHAL(ss << evalcode << assetFuncId << version); break; case 's': case 'b': case 'S': case 'B': - vopret = E_MARSHAL(ss << evalcode << assetFuncId << version << unit_price << origpubkey); + vopret = E_MARSHAL(ss << evalcode << assetFuncId << version << unit_price << origpubkey << expiryHeight); break; - case 'E': case 'e': + /*case 'E': case 'e': assetid2 = revuint256(assetid2); - vopret = E_MARSHAL(ss << evalcode << assetFuncId << version << assetid2 << unit_price << origpubkey); - break; + vopret = E_MARSHAL(ss << evalcode << assetFuncId << version << expiryHeight << unit_price << origpubkey); + break;*/ default: CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%s illegal funcid.%02x\n", __func__, assetFuncId); break; @@ -43,7 +46,7 @@ vscript_t EncodeAssetOpRetV1(uint8_t assetFuncId, uint256 assetid2, int64_t unit return(vopret); } -uint8_t DecodeAssetTokenOpRetV1(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &unit_price, std::vector &origpubkey) +uint8_t DecodeAssetTokenOpRetV1(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, CAmount &unit_price, vscript_t &origpubkey, int32_t &expiryHeight) { vscript_t vopretAssets; //, vopretAssetsStripped; uint8_t *script, funcId = 0, assetsFuncId = 0, dummyAssetFuncId, dummyEvalCode, version; @@ -52,7 +55,6 @@ uint8_t DecodeAssetTokenOpRetV1(const CScript &scriptPubKey, uint8_t &assetsEval std::vector oprets; tokenid = zeroid; - assetid2 = zeroid; unit_price = 0; assetsEvalCode = 0; assetsFuncId = 0; @@ -88,13 +90,13 @@ uint8_t DecodeAssetTokenOpRetV1(const CScript &scriptPubKey, uint8_t &assetsEval } 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 >> version; ss >> unit_price; ss >> origpubkey; ss >> expiryHeight) != 0) { //fprintf(stderr,"DecodeAssetTokenOpRet() got price %lld\n",(long long)price); return(assetsFuncId); } break; - case 'E': case 'e': + /*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) { @@ -102,7 +104,7 @@ uint8_t DecodeAssetTokenOpRetV1(const CScript &scriptPubKey, uint8_t &assetsEval assetid2 = revuint256(assetid2); return(assetsFuncId); } - break; + break;*/ default: break; } @@ -114,7 +116,7 @@ uint8_t DecodeAssetTokenOpRetV1(const CScript &scriptPubKey, uint8_t &assetsEval } -vscript_t EncodeAssetOpRetV2(uint8_t assetFuncId, uint256 assetid2, int64_t unit_price, std::vector origpubkey) +vscript_t EncodeAssetOpRetV2(uint8_t assetFuncId, CAmount unit_price, vscript_t origpubkey, int32_t expiryHeight) { vscript_t vopret; uint8_t evalcode = EVAL_ASSETSV2; @@ -127,12 +129,13 @@ vscript_t EncodeAssetOpRetV2(uint8_t assetFuncId, uint256 assetid2, int64_t unit vopret = E_MARSHAL(ss << evalcode << assetFuncId << version); break; case 's': case 'b': case 'S': case 'B': - vopret = E_MARSHAL(ss << evalcode << assetFuncId << version << unit_price << origpubkey); + vopret = E_MARSHAL(ss << evalcode << assetFuncId << version << unit_price << origpubkey << expiryHeight); break; + /* swaps not implemented case 'E': case 'e': assetid2 = revuint256(assetid2); vopret = E_MARSHAL(ss << evalcode << assetFuncId << version << assetid2 << unit_price << origpubkey); - break; + break;*/ default: CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%s illegal funcid.%02x\n", __func__, assetFuncId); break; @@ -140,7 +143,7 @@ vscript_t EncodeAssetOpRetV2(uint8_t assetFuncId, uint256 assetid2, int64_t unit return(vopret); } -uint8_t DecodeAssetTokenOpRetV2(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &unit_price, std::vector &origpubkey) +uint8_t DecodeAssetTokenOpRetV2(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, CAmount &unit_price, vscript_t &origpubkey, int32_t &expiryHeight) { vscript_t vopretAssets; //, vopretAssetsStripped; uint8_t *script, funcId = 0, assetsFuncId = 0, dummyAssetFuncId, dummyEvalCode, version; @@ -149,7 +152,6 @@ uint8_t DecodeAssetTokenOpRetV2(const CScript &scriptPubKey, uint8_t &assetsEval std::vector oprets; tokenid = zeroid; - assetid2 = zeroid; unit_price = 0; assetsEvalCode = 0; assetsFuncId = 0; @@ -185,13 +187,13 @@ uint8_t DecodeAssetTokenOpRetV2(const CScript &scriptPubKey, uint8_t &assetsEval } 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 >> version; ss >> unit_price; ss >> origpubkey; ss >> expiryHeight) != 0) { //fprintf(stderr,"DecodeAssetTokenOpRet() got price %lld\n",(long long)price); return(assetsFuncId); } break; - case 'E': case 'e': + /* 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) { @@ -199,7 +201,7 @@ uint8_t DecodeAssetTokenOpRetV2(const CScript &scriptPubKey, uint8_t &assetsEval assetid2 = revuint256(assetid2); return(assetsFuncId); } - break; + break; */ default: break; } @@ -213,9 +215,9 @@ uint8_t DecodeAssetTokenOpRetV2(const CScript &scriptPubKey, uint8_t &assetsEval // 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(CAmount unit_price, CAmount remaining_nValue, CAmount orig_nValue, CAmount received_nValue, CAmount paid_units) { - int64_t received_unit_price; + CAmount received_unit_price; // int64_t new_unit_price = 0; if (orig_nValue == 0 || received_nValue == 0 || paid_units == 0) { @@ -254,7 +256,7 @@ bool ValidateBidRemainder(CAmount unit_price, int64_t remaining_nValue, int64_t // 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(CAmount unit_price, CAmount &received_nValue, CAmount orig_nValue, CAmount &paid_units, CAmount orig_units, CAmount paid_unit_price) { // int64_t remaining_nValue; @@ -271,7 +273,7 @@ bool SetBidFillamounts(CAmount unit_price, int64_t &received_nValue, int64_t ori // 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__); + CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%s not enough units!\n", __func__); return(false); } //remaining_units = (orig_units - paid_units); @@ -280,8 +282,8 @@ bool SetBidFillamounts(CAmount unit_price, int64_t &received_nValue, int64_t ori //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); + CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%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) { CAmount remaining_nValue = (orig_nValue - received_nValue); @@ -289,7 +291,7 @@ bool SetBidFillamounts(CAmount unit_price, int64_t &received_nValue, int64_t ori } else { - fprintf(stderr, "%s incorrect values: unit_price %lld > 0, orig_value.%lld >= received_value.%lld\n", __func__, + CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%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); } @@ -299,7 +301,7 @@ bool SetBidFillamounts(CAmount unit_price, int64_t &received_nValue, int64_t ori // 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(CAmount unit_price, CAmount fill_assetoshis, CAmount orig_assetoshis, CAmount paid_nValue) { //int64_t remaining_assetoshis; //double dunit_price; @@ -318,11 +320,11 @@ bool SetAskFillamounts(CAmount unit_price, int64_t fill_assetoshis, int64_t orig return(true); }*/ if (orig_assetoshis == 0) { - fprintf(stderr, "%s ask order empty!\n", __func__); + CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%s ask order empty!\n", __func__); return false; } if (fill_assetoshis == 0) { - fprintf(stderr, "%s ask fill tokens is null!\n", __func__); + CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%s ask fill tokens is null!\n", __func__); return false; } CAmount paid_unit_price = paid_nValue / fill_assetoshis; @@ -331,18 +333,18 @@ bool SetAskFillamounts(CAmount unit_price, int64_t fill_assetoshis, int64_t orig //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); + CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%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__); + CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%s ask order totally filled!\n", __func__); return ValidateAskRemainder(unit_price, remaining_assetoshis, orig_assetoshis, fill_assetoshis, paid_nValue); } else { - fprintf(stderr, "%s incorrect values paid_unit_price %lld > 0, fill_assetoshis %lld > 0 and <= orig_assetoshis %lld\n", __func__, + CCLogPrintF(ccassets_log, CCLOG_DEBUG1, "%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); } @@ -351,9 +353,9 @@ bool SetAskFillamounts(CAmount unit_price, int64_t fill_assetoshis, int64_t orig // 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(CAmount unit_price, CAmount remaining_assetoshis, CAmount orig_assetoshis, CAmount received_assetoshis, CAmount paid_nValue) { - int64_t paid_unit_price; + CAmount paid_unit_price; //CAmount unit_price = AssetsGetUnitPrice(ordertxid); //int64_t new_unit_price = 0; @@ -458,29 +460,60 @@ bool ValidateSwapRemainder(int64_t remaining_price, int64_t remaining_nValue, in return(true); } -// get tx's vin inputs for cp->evalcode and addr. If addr is null then all inputs are added -CAmount AssetsGetCCInputs(struct CCcontract_info *cp, const char *addr, const CTransaction &tx) +// get tx's vin inputs for cp->evalcode and addr +CAmount AssetsGetTxCCInputs(Eval *eval, 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)) { - if (myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)) + if (eval->GetTxUnconfirmed(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; + if (Getscriptaddress(scriptaddr, vinTx.vout[tx.vin[i].prevout.n].scriptPubKey) && strcmp(scriptaddr, addr) == 0) { inputs += vinTx.vout[tx.vin[i].prevout.n].nValue; } } } } return inputs; +} + +CAmount AssetsGetTxTokenInputs(Eval *eval, struct CCcontract_info *cpTokens, const CTransaction &tx) +{ + CTransaction vinTx; + uint256 hashBlock; + CAmount inputs = 0LL; + + for (int32_t i = 0; i < tx.vin.size(); i++) + { + if (cpTokens->ismyvin(tx.vin[i].scriptSig)) + { + if (eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock)) + { + inputs += vinTx.vout[tx.vin[i].prevout.n].nValue; + } + } + } + return inputs; +} + +// check if either royalty or paid_value is dust in fill ask +// nOutputValue is the total amount of paid_value + royalty +// it is expected that 0 < royaltyFract < TKLROYALTY_DIVISOR (calling validation code should provide that) +bool AssetsFillOrderIsDust(int32_t royaltyFract, CAmount nOutputValue, bool &isRoyaltyDust) +{ + // nOutputValue is sum of paid_value + royalty_value + // check whether any of them is assets' dust (calc min of royalty and paid_value, compare with assets' dust): + if (nOutputValue / (int64_t)TKLROYALTY_DIVISOR * std::min(royaltyFract, (int32_t)TKLROYALTY_DIVISOR - royaltyFract) <= ASSETS_NORMAL_DUST) { + // decide who should receive nOutputValue if one of values is dust + isRoyaltyDust = royaltyFract < (int64_t)TKLROYALTY_DIVISOR / 2 ? true : false; + //std::cerr << __func__ << " new calc, nOutputValue=" << nOutputValue << " test dust=" << nOutputValue / (int64_t)TKLROYALTY_DIVISOR * std::min(royaltyFract, (int32_t)TKLROYALTY_DIVISOR - royaltyFract) << " isRoyaltyDust=" << isRoyaltyDust << std::endl; + return true; + } + return false; } \ No newline at end of file diff --git a/src/cc/CCassetstx_impl.h b/src/cc/CCassetstx_impl.h index fb9adc2d551..348b393de0b 100644 --- a/src/cc/CCassetstx_impl.h +++ b/src/cc/CCassetstx_impl.h @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2022 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -18,31 +18,33 @@ #include "CCtokens.h" #include "CCassets.h" -#include "CCNFTData.h" +#include "CCTokelData.h" - -static bool IsTxidInActiveChain(uint256 txid) +template +UniValue AssetOrders(uint256 refassetid, const CPubKey &mypk, const UniValue ¶ms) { - CTransaction tx; - uint256 hashBlock; - AssertLockHeld(cs_main); - - if (myGetTransaction(txid, tx, hashBlock)) + UniValue result(UniValue::VARR); + const char *funcname = __func__; + const bool CC_OUTPUTS_TRUE = true; + + int32_t beginHeight = 0; + int32_t endHeight = 0; + CPubKey checkPK = mypk; + std::string checkAddr; + if (params.exists("beginHeight")) + beginHeight = atoi(params["beginHeight"].getValStr().c_str()); + if (params.exists("endHeight")) + endHeight = atoi(params["endHeight"].getValStr().c_str()); + if (params.exists("pubkey")) + checkPK = pubkey2pk(ParseHex(params["pubkey"].getValStr().c_str())); + + if (beginHeight > 0 || endHeight > 0) { - BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end() && (*mi).second) { - CBlockIndex* pindex = (*mi).second; - if (chainActive.Contains(pindex)) - return true; + if (endHeight <= 0) { + LOCK(cs_main); + endHeight = chainActive.Height(); // if beginheight set but endHeight unset then set endHeight to the tip } } - return false; -} - -template -UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t evalcodeNFT) -{ - UniValue result(UniValue::VARR); struct CCcontract_info *cpAssets, assetsC; struct CCcontract_info *cpTokens, tokensC; @@ -50,160 +52,240 @@ UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t evalcodeNFT) cpAssets = CCinit(&assetsC, A::EvalCode()); cpTokens = CCinit(&tokensC, T::EvalCode()); - auto addOrders = [&](struct CCcontract_info *cp, std::vector >::const_iterator it) + auto addOrders = [&](struct CCcontract_info *cp, uint256 ordertxid) { - uint256 txid, hashBlock, assetid, assetid2; - int64_t unit_price; - std::vector origpubkey; + uint256 hashBlock, assetid; + CAmount unit_price; + vscript_t vorigpubkey; CTransaction ordertx; uint8_t funcid, evalCode; - char numstr[32], funcidstr[16], origaddr[KOMODO_ADDRESS_BUFSIZE], origtokenaddr[KOMODO_ADDRESS_BUFSIZE]; + char origaddr[KOMODO_ADDRESS_BUFSIZE], origtokenaddr[KOMODO_ADDRESS_BUFSIZE]; + int32_t expiryHeight; - 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_log, CCLOG_DEBUG2, stream << funcname << " checking txid=" << ordertxid.GetHex() << std::endl); + if (!myGetTransaction(ordertxid, ordertx, hashBlock)) { + LOGSTREAM(ccassets_log, CCLOG_DEBUG2, stream << funcname <<" could not load order txid=" << ordertxid.GetHex() << std::endl); + return; + } + + if (ordertx.vout.size() > 1 && (funcid = A::DecodeAssetTokenOpRet(ordertx.vout.back().scriptPubKey, evalCode, assetid, unit_price, vorigpubkey, expiryHeight)) != 0) { - if (ordertx.vout.size() > ASSETS_GLOBALADDR_VOUT && (funcid = A::DecodeAssetTokenOpRet(ordertx.vout.back().scriptPubKey, evalCode, assetid, assetid2, unit_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_log, CCLOG_DEBUG2, stream << funcname << " 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 ((!checkPK.IsValid() || checkPK == pubkey2pk(vorigpubkey)) && (refassetid.IsNull() || assetid == refassetid)) + { + uint256 spenttxid; + uint256 init_txid = ordertxid; + int32_t spentvin; + int32_t height; + // try to get unspent partially filled order (if it is a search by global assets address) + while(CCgetspenttxid(spenttxid, spentvin, height, init_txid, ASSETS_GLOBALADDR_VOUT) == 0) { - - 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); - if (ordertx.vout[it->first.index].nValue == 0) { - LOGSTREAM(ccassets_log, CCLOG_DEBUG2, stream << "addOrders() order with value=0 skipped" << std::endl); + { + LOCK(cs_main); + if (!IsTxidInActiveChain(spenttxid)) break; + } + init_txid = spenttxid; + } + if (init_txid != ordertxid) { + // if it is a filled order load it + ordertxid = init_txid; + if (!myGetTransaction(ordertxid, ordertx, hashBlock)) { + LOGSTREAM(ccassets_log, CCLOG_DEBUG2, stream << funcname << " could not load order txid=" << ordertxid.GetHex() << std::endl); return; } + if ((funcid = A::DecodeAssetTokenOpRet(ordertx.vout.back().scriptPubKey, evalCode, assetid, unit_price, vorigpubkey, expiryHeight)) == 0) { + LOGSTREAM(ccassets_log, CCLOG_DEBUG2, stream << funcname << " could not decode order txid=" << ordertxid.GetHex() << std::endl); + return; + } + } - UniValue item(UniValue::VOBJ); + if (ordertx.vout.size() < 2) { + LOGSTREAM(ccassets_log, CCLOG_DEBUG2, stream << funcname << " txid skipped " << ordertxid.GetHex() << std::endl); + return; + } - 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') + UniValue item(UniValue::VOBJ); + + std::string funcidstr(1, (char)funcid); + item.push_back(Pair("funcid", funcidstr)); + item.push_back(Pair("txid", ordertxid.GetHex())); + if (funcid == 'b' || funcid == 'B') + { + item.push_back(Pair("bidamount", ValueFromAmount(ordertx.vout[0].nValue))); + } + else if (funcid == 's' || funcid == 'S') + { + item.push_back(Pair("askamount", ordertx.vout[0].nValue)); + } + else + return; + if (vorigpubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) + { + GetCCaddress(cp, origaddr, pubkey2pk(vorigpubkey), A::IsMixed()); + item.push_back(Pair("origaddress", origaddr)); + GetTokensCCaddress(cpTokens, origtokenaddr, pubkey2pk(vorigpubkey), A::IsMixed()); + item.push_back(Pair("origtokenaddress", origtokenaddr)); + } + if (assetid != zeroid) + item.push_back(Pair("tokenid", assetid.GetHex())); + if (unit_price > 0) + { + if (funcid == 's' || funcid == 'S' /*|| funcid == 'e' || funcid == 'E' not supported */) { - 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)); + item.push_back(Pair("totalrequired", ValueFromAmount(unit_price * ordertx.vout[0].nValue))); + item.push_back(Pair("price", ValueFromAmount(unit_price))); } - else + else if (funcid == 'b' || funcid == 'B') { - sprintf(numstr, "%lld", (long long)ordertx.vout[it->first.index].nValue); - item.push_back(Pair("amount", numstr)); - sprintf(numstr, "%lld", (long long)ordertx.vout[0].nValue); - item.push_back(Pair("askamount", numstr)); + item.push_back(Pair("totalrequired", unit_price ? ordertx.vout[0].nValue / unit_price : 0)); + item.push_back(Pair("price", ValueFromAmount(unit_price))); } - if (origpubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) + } + { + LOCK(cs_main); + CBlockIndex *pindex = komodo_getblockindex(hashBlock); + if (pindex) + item.push_back(Pair("blockHeight", pindex->GetHeight())); + } + if (expiryHeight > 0) + item.push_back(Pair("ExpiryHeight", expiryHeight)); + + if (ordertx.vout[0].nValue > 0LL) // do not add totally filled orders + result.push_back(item); + LOGSTREAM(ccassets_log, CCLOG_DEBUG1, stream << funcname << " added order funcId=" << (char)(funcid ? funcid : ' ') << " orderid=" << ordertxid.GetHex() << " tokenid=" << assetid.GetHex() << std::endl); + } + } + }; + + if (!checkPK.IsValid()) // get tokenorders (all orders) + { + if (beginHeight > 0 || endHeight > 0) + { + // tokenbids (using addressindex sorted by height): + std::vector> addressIndexOutputsCoins; + char assetsGlobalAddr[KOMODO_ADDRESS_BUFSIZE]; + GetCCaddress(cpAssets, assetsGlobalAddr, GetUnspendable(cpAssets, NULL), A::IsMixed()); + SetAddressIndexOutputs(addressIndexOutputsCoins, assetsGlobalAddr, CC_OUTPUTS_TRUE, beginHeight, endHeight); + LOGSTREAMFN(ccassets_log, CCLOG_DEBUG1, stream << "SetAddressIndexOutputs addressIndexOutputsCoins.size()=" << addressIndexOutputsCoins.size() << std::endl); + for (const auto &outputsCoins : addressIndexOutputsCoins) + { + if (!outputsCoins.first.spending) { + bool isTxidInActiveChain = false; { - GetCCaddress(cp, origaddr, pubkey2pk(origpubkey), A::IsMixed()); - item.push_back(Pair("origaddress", origaddr)); - GetTokensCCaddress(cpTokens, origtokenaddr, pubkey2pk(origpubkey), A::IsMixed()); - item.push_back(Pair("origtokenaddress", origtokenaddr)); + LOCK(cs_main); + isTxidInActiveChain = IsTxidInActiveChain(outputsCoins.first.txhash); } - if (assetid != zeroid) - item.push_back(Pair("tokenid", assetid.GetHex())); - if (assetid2 != zeroid) - item.push_back(Pair("otherid", assetid2.GetHex())); - if (unit_price > 0) + if (isTxidInActiveChain) + addOrders(cpAssets, outputsCoins.first.txhash); + } + } + + // tokenasks (using addressindex sorted by height): + std::vector> addressIndexOutputsTokens; + char tokensAssetsGlobalAddr[KOMODO_ADDRESS_BUFSIZE]; + GetTokensCCaddress(cpAssets, tokensAssetsGlobalAddr, GetUnspendable(cpAssets, NULL), A::IsMixed()); + SetAddressIndexOutputs(addressIndexOutputsTokens, tokensAssetsGlobalAddr, CC_OUTPUTS_TRUE, beginHeight, endHeight); + LOGSTREAMFN(ccassets_log, CCLOG_DEBUG1, stream << "SetAddressIndexOutputs addressIndexOutputsTokens.size()=" << addressIndexOutputsTokens.size() << std::endl); + for (const auto &outputsTokens : addressIndexOutputsTokens) + { + if (!outputsTokens.first.spending) { + bool isTxidInActiveChain = false; { - if (funcid == 's' || funcid == 'S' || funcid == 'e' || funcid == 'E') - { - sprintf(numstr, "%.8f", (double)unit_price * ordertx.vout[ASSETS_GLOBALADDR_VOUT].nValue / 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))); - } - 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))); - } + LOCK(cs_main); + isTxidInActiveChain = IsTxidInActiveChain(outputsTokens.first.txhash); } - 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); + if (isTxidInActiveChain) + addOrders(cpAssets, outputsTokens.first.txhash); + } + } + } + else + { + // tokenbids: + std::vector > unspentOutputsCoins; + char assetsGlobalAddr[KOMODO_ADDRESS_BUFSIZE]; + GetCCaddress(cpAssets, assetsGlobalAddr, GetUnspendable(cpAssets, NULL), A::IsMixed()); + SetCCunspents(unspentOutputsCoins, assetsGlobalAddr, CC_OUTPUTS_TRUE); + for (const auto & unspentCoins : unspentOutputsCoins) + { + bool isTxidInActiveChain = false; + { + LOCK(cs_main); + isTxidInActiveChain = IsTxidInActiveChain(unspentCoins.first.txhash); } + if (isTxidInActiveChain) + addOrders(cpAssets, unspentCoins.first.txhash); + } + + // tokenasks: + std::vector > unspentOutputsTokens; + char tokensAssetsGlobalAddr[KOMODO_ADDRESS_BUFSIZE]; + GetTokensCCaddress(cpAssets, tokensAssetsGlobalAddr, GetUnspendable(cpAssets, NULL), A::IsMixed()); + SetCCunspents(unspentOutputsTokens, tokensAssetsGlobalAddr, CC_OUTPUTS_TRUE); + for (const auto & unspentTokens : unspentOutputsTokens) + { + bool isTxidInActiveChain = false; + { + LOCK(cs_main); + isTxidInActiveChain = IsTxidInActiveChain(unspentTokens.first.txhash); + } + if (isTxidInActiveChain) + addOrders(cpAssets, unspentTokens.first.txhash); } } - }; - - std::vector > unspentOutputsTokens, unspentOutputsNFTs, unspentOutputsCoins; - - char assetsUnspendableAddr[KOMODO_ADDRESS_BUFSIZE]; - GetCCaddress(cpAssets, assetsUnspendableAddr, GetUnspendable(cpAssets, NULL), A::IsMixed()); - SetCCunspents(unspentOutputsCoins, assetsUnspendableAddr, true); - - char assetsTokensUnspendableAddr[KOMODO_ADDRESS_BUFSIZE]; - TokenDataTuple tokenData; - vuint8_t vopretNFT; - if (refassetid != zeroid) { - GetTokenData(refassetid, tokenData, vopretNFT); - if (vopretNFT.size() > 0) - cpAssets->evalcodeNFT = vopretNFT.begin()[0]; } - GetTokensCCaddress(cpAssets, assetsTokensUnspendableAddr, GetUnspendable(cpAssets, NULL), A::IsMixed()); - 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 (evalcodeNFT != 0) { //this would be mytokenorders - char assetsNFTUnspendableAddr[KOMODO_ADDRESS_BUFSIZE]; - - // 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), A::IsMixed()); - SetCCunspents(unspentOutputsNFTs, assetsNFTUnspendableAddr,true); - - for (std::vector >::const_iterator itNFTs = unspentOutputsNFTs.begin(); - itNFTs != unspentOutputsNFTs.end(); - itNFTs++) - addOrders(cpAssets, itNFTs); + else + { + // mytokenorders, use marker on my pk : + std::vector > unspentsMyAddr; + char assetsMyAddr[KOMODO_ADDRESS_BUFSIZE]; + GetCCaddress1of2(cpAssets, assetsMyAddr, checkPK, GetUnspendable(cpAssets, NULL), A::IsMixed()); + SetCCunspents(unspentsMyAddr, assetsMyAddr, true); + for (const auto & orders : unspentsMyAddr) + { + bool isTxidInActiveChain = false; + { + LOCK(cs_main); + isTxidInActiveChain = IsTxidInActiveChain(orders.first.txhash); + } + // also check begin/end heights: + if (isTxidInActiveChain && (beginHeight <= 0 || orders.second.blockHeight >= beginHeight) && (endHeight <= 0 || orders.second.blockHeight <= endHeight)) + addOrders(cpAssets, orders.first.txhash); + } } - return(result); + return result; } // rpc tokenbid implementation, locks 'bidamount' coins for the 'pricetotal' of tokens template -UniValue CreateBuyOffer(const CPubKey &mypk, int64_t txfee, int64_t bidamount, uint256 assetid, int64_t numtokens) +UniValue CreateBuyOffer(const CPubKey &mypk, CAmount txfee, CAmount bidamount, uint256 assetid, CAmount numtokens, int32_t expiryHeight) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); struct CCcontract_info *cpAssets, C; uint256 hashBlock; CTransaction vintx; - std::vector origpubkey; + std::vector vorigpubkey; std::string name,description; - int64_t inputs; + CAmount inputs; std::vector oprets; - if (bidamount <= 0 || numtokens <= 0) - { + if (bidamount <= 0 || numtokens <= 0) { CCerror = "invalid bidamount or numtokens"; return(""); } + CAmount unit_price = bidamount / numtokens; + if (unit_price <= 0) { + CCerror = "invalid bid params"; + return (""); + } // check if valid token - if (myGetTransaction(assetid, vintx, hashBlock) == 0) - { + if (myGetTransaction(assetid, vintx, hashBlock) == 0) { CCerror = "could not find assetid\n"; return(""); } - if (vintx.vout.size() == 0 || T::DecodeTokenCreateOpRet(vintx.vout.back().scriptPubKey, origpubkey, name, description, oprets) == 0) - { + if (vintx.vout.size() == 0 || T::DecodeTokenCreateOpRet(vintx.vout.back().scriptPubKey, vorigpubkey, name, description, oprets) == 0) { CCerror = "assetid isn't token creation txid\n"; return(""); } @@ -220,15 +302,13 @@ UniValue CreateBuyOffer(const CPubKey &mypk, int64_t txfee, int64_t bidamount, u return (""); } - CAmount unit_price = bidamount / numtokens; - CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, 0); mtx.vout.push_back(T::MakeCC1vout(A::EvalCode(), bidamount, unspendableAssetsPubkey)); - mtx.vout.push_back(T::MakeCC1vout(A::EvalCode(), ASSETS_MARKER_AMOUNT, mypk)); + mtx.vout.push_back(T::MakeCC1of2vout(A::EvalCode(), ASSETS_MARKER_AMOUNT, mypk, unspendableAssetsPubkey)); // 1of2 marker for my orders - UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), 0, cpAssets, mtx, mypk, txfee, + UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), FINALIZECCTX_NO_CHANGE_WHEN_DUST, cpAssets, mtx, mypk, txfee, T::EncodeTokenOpRet(assetid, {}, // TODO: actually this tx is not 'tokens', maybe it is better not to have token opret here but only asset opret. - { A::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 + { A::EncodeAssetOpRet('b', unit_price, vuint8_t(mypk.begin(), mypk.end()), expiryHeight) } )); // 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; @@ -240,11 +320,9 @@ UniValue CreateBuyOffer(const CPubKey &mypk, int64_t txfee, int64_t bidamount, u // rpc tokenask implementation, locks 'numtokens' tokens for the 'askamount' template -UniValue CreateSell(const CPubKey &mypk, int64_t txfee, int64_t numtokens, uint256 assetid, int64_t askamount) +UniValue CreateSell(const CPubKey &mypk, CAmount txfee, CAmount numtokens, uint256 assetid, CAmount askamount, int32_t expiryHeight) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - uint64_t mask; - int64_t inputs, CCchange; struct CCcontract_info *cpAssets, assetsC; struct CCcontract_info *cpTokens, tokensC; @@ -260,36 +338,38 @@ UniValue CreateSell(const CPubKey &mypk, int64_t txfee, int64_t numtokens, uint2 if (AddNormalinputsRemote(mtx, mypk, txfee+ASSETS_MARKER_AMOUNT, 0x10000) > 0) // use AddNormalinputsRemote to sign with mypk { - mask = ~((1LL << mtx.vin.size()) - 1); + CAmount inputs; // add single-eval tokens (or non-fungible tokens): cpTokens = CCinit(&tokensC, T::EvalCode()); // NOTE: adding inputs only from EVAL_TOKENS cc - if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, numtokens, 60, false)) > 0) + if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, numtokens, 0x1000, false)) > 0LL) { if (inputs < numtokens) { - CCerror = strprintf("insufficient tokens for ask"); + CCerror = "insufficient tokens for ask"; return (""); } CAmount unit_price = askamount / numtokens; - - uint8_t evalcodeNFT = cpTokens->evalcodeNFT ? cpTokens->evalcodeNFT : 0; + if (unit_price <= 0) { + CCerror = "invalid ask params"; + return (""); + } CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, NULL); - mtx.vout.push_back(T::MakeTokensCC1vout(A::EvalCode(), evalcodeNFT, numtokens, unspendableAssetsPubkey)); - mtx.vout.push_back(T::MakeCC1vout(A::EvalCode(), ASSETS_MARKER_AMOUNT, mypk)); //marker (seems, it is not for my tokenorders, not used yet) - if (inputs > numtokens) - CCchange = (inputs - numtokens); - if (CCchange != 0) + mtx.vout.push_back(T::MakeTokensCC1vout(A::EvalCode(), numtokens, unspendableAssetsPubkey)); + mtx.vout.push_back(T::MakeCC1of2vout(A::EvalCode(), ASSETS_MARKER_AMOUNT, mypk, unspendableAssetsPubkey)); // 1of2 marker (it is for my tokenorders) + CAmount CCchange = inputs - numtokens; + if (CCchange != 0LL) { // change to single-eval or non-fungible token vout (although for non-fungible token change currently is not possible) - mtx.vout.push_back(T::MakeTokensCC1vout(evalcodeNFT ? evalcodeNFT : T::EvalCode(), CCchange, mypk)); + mtx.vout.push_back(T::MakeTokensCC1vout(T::EvalCode(), CCchange, mypk)); + } // cond to spend NFT from mypk - CCwrapper wrCond(T::MakeTokensCCcond1(evalcodeNFT, mypk)); + CCwrapper wrCond(T::MakeTokensCCcond1(T::EvalCode(), mypk)); CCAddVintxCond(cpTokens, wrCond, NULL); //NULL indicates to use myprivkey - UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), mask, cpTokens, mtx, mypk, txfee, + UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), FINALIZECCTX_NO_CHANGE_WHEN_DUST, cpTokens, mtx, mypk, txfee, T::EncodeTokenOpRet(assetid, { unspendableAssetsPubkey }, - { A::EncodeAssetOpRet('s', zeroid, unit_price, vuint8_t(mypk.begin(), mypk.end()) ) } )); + { A::EncodeAssetOpRet('s', unit_price, vuint8_t(mypk.begin(), mypk.end()), expiryHeight) } )); if (!ResultHasTx(sigData)) return MakeResultError("Could not finalize tx"); return sigData; @@ -305,11 +385,12 @@ UniValue CreateSell(const CPubKey &mypk, int64_t txfee, int64_t numtokens, uint2 } ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// +/* template 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; + CPubKey mypk; int64_t inputs,CCchange; CScript opret; struct CCcontract_info *cp,C; ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// fprintf(stderr,"asset swaps disabled\n"); @@ -330,8 +411,7 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a if (AddNormalinputs(mtx, mypk, txfee, 0x10000) > 0) { - mask = ~((1LL << mtx.vin.size()) - 1); - /*if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0) + if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0) { ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// if (inputs < askamount) { @@ -361,26 +441,27 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a EncodeAssetOpRet('e', assetid2, pricetotal, Mypubkey())); } ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// - return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret)); + return(FinalizeCCTx(FINALIZECCTX_NO_CHANGE_WHEN_DUST,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///////////////////////////////// +} */ +////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// // unlocks coins, ends bid order template -UniValue CancelBuyOffer(const CPubKey &mypk, int64_t txfee,uint256 assetid,uint256 bidtxid) +UniValue CancelBuyOffer(const CPubKey &mypk, CAmount txfee, uint256 assetid, uint256 bidtxid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction vintx; uint64_t mask; - uint256 hashBlock; int64_t bidamount; + CTransaction vintx; + uint256 hashBlock; struct CCcontract_info *cpAssets, C; cpAssets = CCinit(&C, A::EvalCode()); @@ -389,28 +470,32 @@ UniValue CancelBuyOffer(const CPubKey &mypk, int64_t txfee,uint256 assetid,uint2 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) + if (txfee <= ASSETS_MARKER_AMOUNT || AddNormalinputsRemote(mtx, mypk, txfee /*+ ASSETS_MARKER_AMOUNT*/, 0x10000) > 0) { uint256 spendingtxid; int32_t spendingvin, h; - mask = ~((1LL << mtx.vin.size()) - 1); LOCK(cs_main); - if ((CCgetspenttxid(spendingtxid, spendingvin, h, bidtxid, ASSETS_GLOBALADDR_VOUT) != 0 || !IsTxidInActiveChain(spendingtxid)) && myGetTransaction(bidtxid, vintx, hashBlock) && vintx.vout.size() > ASSETS_GLOBALADDR_VOUT) + if ((CCgetspenttxid(spendingtxid, spendingvin, h, bidtxid, ASSETS_GLOBALADDR_VOUT) != 0 || !IsTxidInActiveChain(spendingtxid)) && + myGetTransaction(bidtxid, vintx, hashBlock) && vintx.vout.size() > ASSETS_GLOBALADDR_VOUT) { - uint8_t dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector dummyOrigpubkey; + uint8_t dummyEvalCode; uint256 dummyAssetid; + CAmount dummyPrice; + vscript_t vorigpubkey; + int32_t expiryHeight; + uint8_t unspendableAssetsPrivkey[32]; + CPubKey unspendableAssetsPk; - //std::vector vopretNonfungible; - //GetNonfungibleData(assetid, vopretNonfungible); + unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); - bidamount = vintx.vout[ASSETS_GLOBALADDR_VOUT].nValue; + CAmount bidamount = vintx.vout[ASSETS_GLOBALADDR_VOUT].nValue; if (bidamount == 0) { CCerror = "bid is empty"; return ""; } mtx.vin.push_back(CTxIn(bidtxid, ASSETS_GLOBALADDR_VOUT, CScript())); // coins in Assets - uint8_t funcid = A::DecodeAssetTokenOpRet(vintx.vout.back().scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey); + uint8_t funcid = A::DecodeAssetTokenOpRet(vintx.vout.back().scriptPubKey, dummyEvalCode, dummyAssetid, dummyPrice, vorigpubkey, expiryHeight); 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) @@ -421,18 +506,25 @@ UniValue CancelBuyOffer(const CPubKey &mypk, int64_t txfee,uint256 assetid,uint2 } if (bidamount > ASSETS_NORMAL_DUST) - mtx.vout.push_back(CTxOut(bidamount, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(bidamount, CScript() << ParseHex(HexStr(vorigpubkey)) << OP_CHECKSIG)); else { // send dust back to global addr - CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, NULL); - mtx.vout.push_back(T::MakeCC1vout(A::EvalCode(), bidamount, unspendableAssetsPubkey)); + mtx.vout.push_back(T::MakeCC1vout(A::EvalCode(), bidamount, unspendableAssetsPk)); + LOGSTREAMFN(ccassets_log, CCLOG_DEBUG1, stream << "remainder dust detected left on global bidamount=" << bidamount << std::endl); } - mtx.vout.push_back(CTxOut(ASSETS_MARKER_AMOUNT, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + // probe to spend marker: + if (mypk == pubkey2pk(vorigpubkey)) { + CCwrapper wrCond(::MakeCCcond1of2(A::EvalCode(), pubkey2pk(vorigpubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond, nullptr); // spend with mypk + } else { + CCwrapper wrCond(::MakeCCcond1of2(A::EvalCode(), pubkey2pk(vorigpubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond, unspendableAssetsPrivkey); // spend with shared pk (for expired orders) + } - UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), mask, cpAssets, mtx, mypk, txfee, + UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), FINALIZECCTX_NO_CHANGE_WHEN_DUST, cpAssets, mtx, mypk, txfee, T::EncodeTokenOpRet(assetid, {}, - { A::EncodeAssetOpRet('o', zeroid, 0, vuint8_t(mypk.begin(), mypk.end())) })); + { A::EncodeAssetOpRet('o', 0, vuint8_t(), 0) })); if (!ResultHasTx(sigData)) return MakeResultError("Could not finalize tx"); return sigData; @@ -447,11 +539,11 @@ UniValue CancelBuyOffer(const CPubKey &mypk, int64_t txfee,uint256 assetid,uint2 //unlocks tokens, ends ask order template -UniValue CancelSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 asktxid) +UniValue CancelSell(const CPubKey &mypk, CAmount txfee, uint256 assetid, uint256 asktxid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction vintx; uint64_t mask; - uint256 hashBlock; int64_t askamount; + CTransaction vintx; + uint256 hashBlock; CAmount askamount; struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC; cpAssets = CCinit(&assetsC, A::EvalCode()); @@ -460,19 +552,19 @@ UniValue CancelSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 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) + if (txfee <= ASSETS_MARKER_AMOUNT || AddNormalinputsRemote(mtx, mypk, txfee, 0x10000) > 0) // if txfee <= ASSETS_MARKER_AMOUNT then take txfee from marker { uint256 spendingtxid; int32_t spendingvin, h; - mask = ~((1LL << mtx.vin.size()) - 1); LOCK(cs_main); if ((CCgetspenttxid(spendingtxid, spendingvin, h, asktxid, ASSETS_GLOBALADDR_VOUT) != 0 || !IsTxidInActiveChain(spendingtxid)) && myGetTransaction(asktxid, vintx, hashBlock) != 0 && vintx.vout.size() > 0) { uint8_t dummyEvalCode; - uint256 dummyAssetid, dummyAssetid2; - int64_t dummyPrice; - std::vector dummyOrigpubkey; + uint256 dummyAssetid; + CAmount dummyPrice; + vscript_t vorigpubkey; + int32_t expiryHeight; askamount = vintx.vout[ASSETS_GLOBALADDR_VOUT].nValue; if (askamount == 0) { @@ -481,7 +573,7 @@ UniValue CancelSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 } mtx.vin.push_back(CTxIn(asktxid, ASSETS_GLOBALADDR_VOUT, CScript())); - uint8_t funcid = A::DecodeAssetTokenOpRet(vintx.vout.back().scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey); + uint8_t funcid = A::DecodeAssetTokenOpRet(vintx.vout.back().scriptPubKey, dummyEvalCode, dummyAssetid, dummyPrice, vorigpubkey, expiryHeight); 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) @@ -490,33 +582,29 @@ UniValue CancelSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 CCerror = "invalid ask tx or not enough vouts"; return ""; } + mtx.vout.push_back(T::MakeTokensCC1vout(T::EvalCode(), askamount, pubkey2pk(vorigpubkey))); // one-eval token vout + // mtx.vout.push_back(CTxOut(ASSETS_MARKER_AMOUNT, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // we dont need marker for cancelled orders - TokenDataTuple tokenData; - vuint8_t vopretNonfungible; - GetTokenData(assetid, tokenData, vopretNonfungible); - if (vopretNonfungible.size() > 0) - cpAssets->evalcodeNFT = vopretNonfungible.begin()[0]; - - mtx.vout.push_back(T::MakeTokensCC1vout(cpAssets->evalcodeNFT ? cpAssets->evalcodeNFT : T::EvalCode(), askamount, mypk)); // one-eval token vout - mtx.vout.push_back(CTxOut(ASSETS_MARKER_AMOUNT, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - - // 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 + uint8_t unspendableAssetsPrivkey[32]; CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); - //GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk, A::IsMixed()); // add additional eval-tokens unspendable assets privkey: - //CCaddr2set(cpAssets, T::EvalCode(), unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); - CCwrapper wrCond(T::MakeTokensCCcond1(A::EvalCode(), cpAssets->evalcodeNFT, unspendableAssetsPk)); + CCwrapper wrCond(T::MakeTokensCCcond1(A::EvalCode(), unspendableAssetsPk)); // probe to spend ask remainder CCAddVintxCond(cpAssets, wrCond, unspendableAssetsPrivkey); - UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), mask, cpAssets, mtx, mypk, txfee, + // probe to spend marker + if (mypk == pubkey2pk(vorigpubkey)) { + CCwrapper wrCond(::MakeCCcond1of2(A::EvalCode(), pubkey2pk(vorigpubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond, nullptr); // spend with mypk + } else { + CCwrapper wrCond(::MakeCCcond1of2(A::EvalCode(), pubkey2pk(vorigpubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond, unspendableAssetsPrivkey); // spend with shared pk (for expired orders) + } + + UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), FINALIZECCTX_NO_CHANGE_WHEN_DUST, cpAssets, mtx, mypk, txfee, T::EncodeTokenOpRet(assetid, { mypk }, - { A::EncodeAssetOpRet('x', zeroid, 0, vuint8_t(mypk.begin(), mypk.end())) } )); + { A::EncodeAssetOpRet('x', 0, vuint8_t(), 0) } )); if (!ResultHasTx(sigData)) return MakeResultError("Could not finalize tx"); return sigData; @@ -531,15 +619,14 @@ UniValue CancelSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 //send tokens, receive coins: template -UniValue FillBuyOffer(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 bidtxid, int64_t fill_units, CAmount paid_unit_price) +UniValue FillBuyOffer(const CPubKey &mypk, CAmount txfee, uint256 assetid, uint256 bidtxid, CAmount fill_units, CAmount paid_unit_price) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction vintx; uint256 hashBlock; - std::vector origpubkey; + std::vector vorigpubkey; const int32_t bidvout = ASSETS_GLOBALADDR_VOUT; - uint64_t mask; - int64_t orig_units, unit_price, bid_amount, paid_amount, remaining_units, inputs, tokensChange = 0; + CAmount orig_units, unit_price, bid_amount, paid_amount, remaining_units, tokenInputs; struct CCcontract_info *cpTokens, tokensC; struct CCcontract_info *cpAssets, assetsC; @@ -550,17 +637,15 @@ UniValue FillBuyOffer(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint2 cpTokens = CCinit(&tokensC, T::EvalCode()); TokenDataTuple tokenData; - vuint8_t vopretNonfungible; - uint8_t evalcodeNFT = 0; - uint64_t royaltyFract = 0; // royaltyFract is N in N/1000 fraction - GetTokenData(assetid, tokenData, vopretNonfungible); - if (vopretNonfungible.size() > 0) { - evalcodeNFT = vopretNonfungible.begin()[0]; - GetNftDataAsUint64(vopretNonfungible, NFTPROP_ROYALTY, royaltyFract); - if (royaltyFract > NFTROYALTY_DIVISOR-1) - royaltyFract = NFTROYALTY_DIVISOR-1; // royalty upper limit + vuint8_t vextraData; + int64_t royaltyFract = 0; // royaltyFract is N in N/1000 fraction + GetTokenData(NULL, assetid, tokenData, vextraData); + if (vextraData.size() > 0) { + GetTokelDataAsInt64(vextraData, TKLPROP_ROYALTY, royaltyFract); + if (royaltyFract > TKLROYALTY_DIVISOR-1) + royaltyFract = TKLROYALTY_DIVISOR-1; // royalty upper limit } - vuint8_t ownerpubkey = std::get<0>(tokenData); + vuint8_t vtokenCreatorPubkey = std::get<0>(tokenData); if (txfee == 0) txfee = 10000; @@ -570,24 +655,31 @@ UniValue FillBuyOffer(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint2 uint256 spendingtxid; int32_t spendingvin, h; - mask = ~((1LL << mtx.vin.size()) - 1); LOCK(cs_main); + int32_t nextHeight = komodo_nextheight(); if ((CCgetspenttxid(spendingtxid, spendingvin, h, bidtxid, bidvout) != 0 || !IsTxidInActiveChain(spendingtxid)) && myGetTransaction(bidtxid, vintx, hashBlock) != 0 && vintx.vout.size() > bidvout) { + uint256 assetidOpret; + int32_t expiryHeight; + bid_amount = vintx.vout[bidvout].nValue; - uint8_t funcid = SetAssetOrigpubkey(origpubkey, unit_price, vintx); // get orig pk, orig units + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, vintx); // get orig pk, orig units if (funcid != 'b' && funcid != 'B') { CCerror = "not an bid order"; return ""; } + if (assetid != assetidOpret) { + CCerror = "invalid tokenid"; + 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, false)) > 0) + if ((tokenInputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fill_units, 0x1000, false)) > 0) { - if (inputs < fill_units) { + if (tokenInputs < fill_units) { CCerror = strprintf("insufficient tokens to fill buy offer"); return (""); } @@ -596,47 +688,65 @@ UniValue FillBuyOffer(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint2 CCerror = "incorrect units or price"; return (""); } - CAmount royaltyValue = royaltyFract > 0 ? paid_amount / NFTROYALTY_DIVISOR * royaltyFract : 0; - - if (inputs > fill_units) - tokensChange = (inputs - fill_units); + CAmount royaltyValue = royaltyFract > 0 ? paid_amount / TKLROYALTY_DIVISOR * royaltyFract : 0; + // check for dust: + //if (royaltyValue <= ASSETS_NORMAL_DUST) + // royaltyValue = 0; + bool hasDust = false; + bool isRoyaltyDust = true; + if (royaltyFract > 0) { + // correct calculation: + if (AssetsFillOrderIsDust(royaltyFract, paid_amount, isRoyaltyDust)) { + royaltyValue = 0; // all amount (with dust) to go to one pk (depending on which is not dust) + hasDust = true; + } + } + + CAmount tokensChange = tokenInputs - fill_units; uint8_t unspendableAssetsPrivkey[32]; cpAssets = CCinit(&assetsC, A::EvalCode()); CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); - - if (orig_units - fill_units > 0 || bid_amount - paid_amount <= ASSETS_NORMAL_DUST) + if (orig_units - fill_units > 0 || bid_amount - paid_amount <= ASSETS_NORMAL_DUST) { // bidder has coins for more tokens or only dust is sent back to global address mtx.vout.push_back(T::MakeCC1vout(A::EvalCode(), bid_amount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder or the dust is sent back to cc global addr - else - mtx.vout.push_back(CTxOut(bid_amount - paid_amount, CScript() << ParseHex(HexStr(origpubkey)) << OP_CHECKSIG)); // vout0 if no more tokens to buy, send the remainder to originator - mtx.vout.push_back(CTxOut(paid_amount - royaltyValue, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // vout1 coins to mypk normal - if (royaltyFract > 0) // note it makes vout even if roaltyValue is 0 - mtx.vout.push_back(CTxOut(royaltyValue, CScript() << ParseHex(HexStr(ownerpubkey)) << OP_CHECKSIG)); // vout2 trade royalty to token owner - mtx.vout.push_back(T::MakeTokensCC1vout(evalcodeNFT ? evalcodeNFT : T::EvalCode(), fill_units, pubkey2pk(origpubkey))); // vout2(3) single-eval tokens sent to the originator - mtx.vout.push_back(T::MakeCC1vout(A::EvalCode(), ASSETS_MARKER_AMOUNT, origpubkey)); // vout3(4 if royalty) marker to origpubkey - - if (tokensChange != 0) - mtx.vout.push_back(T::MakeTokensCC1vout(evalcodeNFT ? evalcodeNFT : T::EvalCode(), tokensChange, mypk)); // change in single-eval tokens - - //fprintf(stderr, "%s remaining_units %lld -> origpubkey\n", __func__, (long long)remaining_units); + if (bid_amount - paid_amount <= ASSETS_NORMAL_DUST) { + LOGSTREAMFN(ccassets_log, CCLOG_DEBUG1, stream << "remainder dust detected, left on global addr (bid_amount - paid_amount)=" << (bid_amount - paid_amount) << std::endl); + std::cerr << __func__ << " bid_amount - paid_amount <= ASSETS_NORMAL_DUST, left on globalpk, bid_amount - paid_amount=" << bid_amount - paid_amount << std::endl; + } + } + else { + mtx.vout.push_back(CTxOut(bid_amount - paid_amount, CScript() << vorigpubkey << OP_CHECKSIG)); // vout0 if no more tokens to buy, send the remainder to originator + std::cerr << __func__ << " no more amount for tokens, sending to vorigpubkey bid_amount - paid_amount=" << bid_amount - paid_amount << std::endl; + } + mtx.vout.push_back(CTxOut(paid_amount - royaltyValue, CScript() << (royaltyFract > 0 && hasDust && !isRoyaltyDust ? vtokenCreatorPubkey : vuint8_t(mypk.begin(), mypk.end())) << OP_CHECKSIG)); // vout1 coins to mypk normal (if value to my pk is dust then send to the token owner) + std::cerr << __func__ << " to owner or mypk, paid_amount - royaltyValue=" << paid_amount - royaltyValue << " toTokenCreator exp=" << (royaltyFract > 0 && hasDust && !isRoyaltyDust) << std::endl; - //char unspendableAssetsAddr[KOMODO_ADDRESS_BUFSIZE]; - //cpAssets = CCinit(&assetsC, A::EvalCode()); - //GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk, A::IsMixed()); + if (royaltyValue > 0) { // note it makes vout even if roaltyValue is 0 + mtx.vout.push_back(CTxOut(royaltyValue, CScript() << vtokenCreatorPubkey << OP_CHECKSIG)); // vout2 trade royalty to token owner + std::cerr << __func__ << " royaltyValue > 0, adding it to vtokenCreatorPubkey royaltyValue=" << royaltyValue << std::endl; + LOGSTREAMFN(ccassets_log, CCLOG_DEBUG1, stream << "royaltyFract=" << royaltyFract << " royaltyValue=" << royaltyValue << " paid_amount - royaltyValue=" << paid_amount - royaltyValue << std::endl); + } + mtx.vout.push_back(T::MakeTokensCC1vout(T::EvalCode(), fill_units, pubkey2pk(vorigpubkey))); // vout2(3) single-eval tokens sent to the originator + if (orig_units - fill_units > 0) // order is not finished yet + mtx.vout.push_back(T::MakeCC1of2vout(A::EvalCode(), ASSETS_MARKER_AMOUNT, vorigpubkey, unspendableAssetsPk)); // vout3(4 if royalty) marker to vorigpubkey - // add additional unspendable addr from Assets: - //CCaddr2set(cpTokens, A::EvalCode(), unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); + if (tokensChange != 0LL) + mtx.vout.push_back(T::MakeTokensCC1vout(T::EvalCode(), tokensChange, mypk)); // change in single-eval tokens CCwrapper wrCond1(MakeCCcond1(A::EvalCode(), unspendableAssetsPk)); // spend coins CCAddVintxCond(cpTokens, wrCond1, unspendableAssetsPrivkey); - CCwrapper wrCond2(T::MakeTokensCCcond1(evalcodeNFT, mypk)); // spend my tokens to fill buy + CCwrapper wrCond2(T::MakeTokensCCcond1(T::EvalCode(), mypk)); // spend my tokens to fill buy CCAddVintxCond(cpTokens, wrCond2, NULL); //NULL indicates to use myprivkey - UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), mask, cpTokens, mtx, mypk, txfee, - T::EncodeTokenOpRet(assetid, { pubkey2pk(origpubkey) }, - { A::EncodeAssetOpRet('B', zeroid, unit_price, origpubkey) })); + // probe to spend marker + CCwrapper wrCond3(::MakeCCcond1of2(A::EvalCode(), pubkey2pk(vorigpubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond3, nullptr); // spend with mypk + + UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), FINALIZECCTX_NO_CHANGE_WHEN_DUST, cpTokens, mtx, mypk, txfee, + T::EncodeTokenOpRet(assetid, { pubkey2pk(vorigpubkey) }, + { A::EncodeAssetOpRet('B', unit_price, vorigpubkey, expiryHeight) })); if (!ResultHasTx(sigData)) return MakeResultError("Could not finalize tx"); return sigData; @@ -657,16 +767,14 @@ UniValue FillBuyOffer(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint2 // send coins, receive tokens template -UniValue FillSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 assetid2, uint256 asktxid, int64_t fillunits, CAmount paid_unit_price) +UniValue FillSell(const CPubKey &mypk, CAmount txfee, uint256 assetid, uint256 asktxid, CAmount fillunits, CAmount paid_unit_price) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction vintx; uint256 hashBlock; - std::vector origpubkey; - //double dprice; - uint64_t mask = 0; + std::vector vorigpubkey; const int32_t askvout = ASSETS_GLOBALADDR_VOUT; - int64_t unit_price, orig_assetoshis, paid_nValue, inputs, CCchange = 0LL; + CAmount unit_price, orig_assetoshis, paid_nValue; struct CCcontract_info *cpAssets, assetsC; if (fillunits < 0) @@ -674,24 +782,17 @@ UniValue FillSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 a CCerror = strprintf("negative fillunits %lld\n",(long long)fillunits); return(""); } - if (assetid2 != zeroid) - { - CCerror = "asset swaps disabled"; - return(""); - } TokenDataTuple tokenData; - vuint8_t vopretNonfungible; - uint8_t evalcodeNFT = 0; - uint64_t royaltyFract = 0; // royaltyFract is N in N/1000 fraction - GetTokenData(assetid, tokenData, vopretNonfungible); - if (vopretNonfungible.size() > 0) { - evalcodeNFT = vopretNonfungible.begin()[0]; - GetNftDataAsUint64(vopretNonfungible, NFTPROP_ROYALTY, royaltyFract); - if (royaltyFract > NFTROYALTY_DIVISOR-1) - royaltyFract = NFTROYALTY_DIVISOR-1; // royalty upper limit + vuint8_t vextraData; + int64_t royaltyFract = 0; // royaltyFract is N in N/1000 fraction + GetTokenData(NULL, assetid, tokenData, vextraData); + if (vextraData.size() > 0) { + GetTokelDataAsInt64(vextraData, TKLPROP_ROYALTY, royaltyFract); + if (royaltyFract > TKLROYALTY_DIVISOR-1) + royaltyFract = TKLROYALTY_DIVISOR-1; // royalty upper limit } - vuint8_t ownerpubkey = std::get<0>(tokenData); + vuint8_t vtokenCreatorPubkey = std::get<0>(tokenData); cpAssets = CCinit(&assetsC, A::EvalCode()); @@ -703,14 +804,19 @@ UniValue FillSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 a LOCK(cs_main); if ((CCgetspenttxid(spendingtxid, spendingvin, h, asktxid, askvout) != 0 || !IsTxidInActiveChain(spendingtxid)) && myGetTransaction(asktxid, vintx, hashBlock) && vintx.vout.size() > askvout) { + int32_t nextHeight = komodo_nextheight(); + uint256 assetidOpret; + int32_t expiryHeight; orig_assetoshis = vintx.vout[askvout].nValue; - uint8_t funcid = SetAssetOrigpubkey(origpubkey, unit_price, vintx); // get orig pk, orig value + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, vintx); // get orig pk, orig value if (funcid != 's' && funcid != 'S') { CCerror = "not an ask order"; return ""; } - - //dprice = (double)orig_nValue / orig_assetoshis; + if (assetid != assetidOpret) { + CCerror = "invalid tokenid"; + return ""; + } if (paid_unit_price <= 0LL) paid_unit_price = unit_price; if (paid_unit_price <= 0LL) { @@ -719,17 +825,27 @@ UniValue FillSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 a } paid_nValue = paid_unit_price * fillunits; - CAmount royaltyValue = royaltyFract > 0 ? paid_nValue / NFTROYALTY_DIVISOR * royaltyFract : 0; - - 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); + CAmount royaltyValue = royaltyFract > 0 ? paid_nValue / TKLROYALTY_DIVISOR * royaltyFract : 0; + // check for dust: + //if (royaltyValue <= ASSETS_NORMAL_DUST) + // royaltyValue = 0; + // more accurate check matching to AssetValidate's check + + // bad calc + //if (royaltyFract > 0 && paid_nValue - royaltyValue <= ASSETS_NORMAL_DUST / royaltyFract * TKLROYALTY_DIVISOR - ASSETS_NORMAL_DUST) // if value paid to seller less than when the royalty is minimum + // royaltyValue = 0LL; + bool hasDust = false; + bool isRoyaltyDust = true; + if (royaltyFract > 0) { + // correct calculation: + if (AssetsFillOrderIsDust(royaltyFract, paid_nValue, isRoyaltyDust)) { + royaltyValue = 0; // all amount (with dust) to go to one pk (depending on which is not dust) + hasDust = true; + } } + + // Use only one AddNormalinputs() in each rpc call to allow payment if user has only single utxo with normal funds + CAmount inputs = AddNormalinputs(mtx, mypk, txfee + ASSETS_MARKER_AMOUNT + paid_nValue, 0x10000, IsRemoteRPCCall()); if (inputs > 0) { if (inputs < paid_nValue) { @@ -740,74 +856,54 @@ UniValue FillSell(const CPubKey &mypk, int64_t txfee, uint256 assetid, uint256 a // 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 { - if (!SetAskFillamounts(unit_price, fillunits, orig_assetoshis, paid_nValue)) { - CCerror = "incorrect units or price"; - return ""; - } + if (!SetAskFillamounts(unit_price, fillunits, orig_assetoshis, paid_nValue)) { + CCerror = "incorrect units or price"; + return ""; } - + if (paid_nValue == 0) { CCerror = "ask totally filled"; return ""; } - if (assetid2 != zeroid && inputs > paid_nValue) - CCchange = (inputs - paid_nValue); - // vout.0 tokens remainder to unspendable cc addr: - mtx.vout.push_back(T::MakeTokensCC1vout(A::EvalCode(), evalcodeNFT, orig_assetoshis - fillunits, GetUnspendable(cpAssets, NULL))); // token remainder on cc global addr + mtx.vout.push_back(T::MakeTokensCC1vout(A::EvalCode(), 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(T::MakeTokensCC1vout(evalcodeNFT ? evalcodeNFT : T::EvalCode(), fillunits, 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 - royaltyValue, CScript() << origpubkey << OP_CHECKSIG)); //vout.2 coins to ask originator's normal addr - if (royaltyFract > 0) // note it makes the vout even if roaltyValue is 0 - mtx.vout.push_back(CTxOut(royaltyValue, CScript() << ownerpubkey << OP_CHECKSIG)); // vout.3 royalty to token owner - } - mtx.vout.push_back(T::MakeCC1vout(A::EvalCode(), ASSETS_MARKER_AMOUNT, origpubkey)); //vout.3(4 if royalty) marker to origpubkey (for my tokenorders?) - - // 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) - } + mtx.vout.push_back(T::MakeTokensCC1vout(T::EvalCode(), fillunits, mypk)); + mtx.vout.push_back(CTxOut(paid_nValue - royaltyValue, CScript() << (royaltyFract > 0 && hasDust && !isRoyaltyDust ? vtokenCreatorPubkey : vorigpubkey) << OP_CHECKSIG)); //vout.2 coins to ask originator's normal addr + if (royaltyValue > 0) { // note it makes the vout even if roaltyValue is 0 + mtx.vout.push_back(CTxOut(royaltyValue, CScript() << vtokenCreatorPubkey << OP_CHECKSIG)); // vout.3 royalty to token owner + LOGSTREAMFN(ccassets_log, CCLOG_DEBUG1, stream << "royaltyFract=" << royaltyFract << " royaltyValue=" << royaltyValue << " paid_nValue - royaltyValue=" << paid_nValue - royaltyValue << std::endl); + } + + if (orig_assetoshis - fillunits > 0) // we dont need the marker if order is filled + mtx.vout.push_back(T::MakeCC1of2vout(A::EvalCode(), ASSETS_MARKER_AMOUNT, vorigpubkey, GetUnspendable(cpAssets, NULL))); //vout.3(4 if royalty) marker to vorigpubkey (for my tokenorders?) - uint8_t unspendableAssetsPrivkey[32]; - // char unspendableAssetsAddr[KOMODO_ADDRESS_BUFSIZE]; // init assets 'unspendable' privkey and pubkey + uint8_t unspendableAssetsPrivkey[32]; CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); - //GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk, A::IsMixed()); - // add additional eval-tokens unspendable assets privkey: - //CCaddr2set(cpAssets, T::EvalCode(), unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); + CCwrapper wrCond1(T::MakeTokensCCcond1(A::EvalCode(), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond1, unspendableAssetsPrivkey); - CCwrapper wrCond(T::MakeTokensCCcond1(A::EvalCode(), evalcodeNFT, unspendableAssetsPk)); - CCAddVintxCond(cpAssets, wrCond, unspendableAssetsPrivkey); - - //cpAssets->evalcodeNFT = evalcodeNFT; // set nft eval for signing + // probe to spend marker + CCwrapper wrCond2(::MakeCCcond1of2(A::EvalCode(), pubkey2pk(vorigpubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond2, nullptr); // spend with mypk - UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), mask, cpAssets, mtx, mypk, txfee, + UniValue sigData = T::FinalizeCCTx(IsRemoteRPCCall(), FINALIZECCTX_NO_CHANGE_WHEN_DUST, cpAssets, mtx, mypk, txfee, T::EncodeTokenOpRet(assetid, { mypk }, - { A::EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, unit_price, origpubkey) } )); + { A::EncodeAssetOpRet('S', unit_price, vorigpubkey, expiryHeight) } )); if (!ResultHasTx(sigData)) return MakeResultError("Could not finalize tx"); return sigData; } else { - CCerror = strprintf("filltx not enough normal utxos"); + CCerror = "filltx not enough normal utxos"; return ""; } } CCerror = "can't get ask tx"; - return(""); + return ""; } #endif // #ifndef CC_ASSETS_TX_IMPL_H diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp index 99c0d4ceea3..26e28346495 100644 --- a/src/cc/CCcustom.cpp +++ b/src/cc/CCcustom.cpp @@ -14,6 +14,7 @@ ******************************************************************************/ #include "key_io.h" + #include "CCinclude.h" #include "CCassets.h" #include "CCfaucet.h" @@ -31,7 +32,7 @@ #include "CCGateways.h" #include "CCtokens.h" #include "CCImportGateway.h" -#include "CCNFTData.h" +#include "CCTokelData.h" /* @@ -58,11 +59,9 @@ // to create a new CCaddr, add to rpcwallet the CCaddress and start with -pubkey= with the pubkey of the new address, with its wif already imported. set normaladdr and CChexstr. run CCaddress and it will print the privkey along with autocorrect the CCaddress. which should then update the CCaddr here -// Assets, aka Tokens +// Assets #define FUNCNAME IsAssetsInput #define EVALCODE EVAL_ASSETS -const char *AssetsCCaddr = "RGKRjeTBw4LYFotSDLT6RWzMHbhXri6BG6"; -const char *AssetsNormaladdr = "RFYE2yL3KknWdHK6uNhvWacYsCUtwzjY3u"; char AssetsCChexstr[67] = { "02adf84e0e075cf90868bd4e3d34a03420e034719649c41f371fc70d8e33aa2702" }; uint8_t AssetsCCpriv[32] = { 0x9b, 0x17, 0x66, 0xe5, 0x82, 0x66, 0xac, 0xb6, 0xba, 0x43, 0x83, 0x74, 0xf7, 0x63, 0x11, 0x3b, 0xf0, 0xf3, 0x50, 0x6f, 0xd9, 0x6b, 0x67, 0x85, 0xf9, 0x7a, 0xf0, 0x54, 0x4d, 0xb1, 0x30, 0x77 }; #include "CCcustom.inc" @@ -72,8 +71,6 @@ uint8_t AssetsCCpriv[32] = { 0x9b, 0x17, 0x66, 0xe5, 0x82, 0x66, 0xac, 0xb6, 0xb // Faucet #define FUNCNAME IsFaucetInput #define EVALCODE EVAL_FAUCET -const char *FaucetCCaddr = "R9zHrofhRbub7ER77B7NrVch3A63R39GuC"; -const char *FaucetNormaladdr = "RKQV4oYs4rvxAWx1J43VnT73rSTVtUeckk"; char FaucetCChexstr[67] = { "03682b255c40d0cde8faee381a1a50bbb89980ff24539cb8518e294d3a63cefe12" }; uint8_t FaucetCCpriv[32] = { 0xd4, 0x4f, 0xf2, 0x31, 0x71, 0x7d, 0x28, 0x02, 0x4b, 0xc7, 0xdd, 0x71, 0xa0, 0x39, 0xc4, 0xbe, 0x1a, 0xfe, 0xeb, 0xc2, 0x46, 0xda, 0x76, 0xf8, 0x07, 0x53, 0x3d, 0x96, 0xb4, 0xca, 0xa0, 0xe9 }; #include "CCcustom.inc" @@ -83,8 +80,6 @@ uint8_t FaucetCCpriv[32] = { 0xd4, 0x4f, 0xf2, 0x31, 0x71, 0x7d, 0x28, 0x02, 0x4 // Rewards #define FUNCNAME IsRewardsInput #define EVALCODE EVAL_REWARDS -const char *RewardsCCaddr = "RTsRBYL1HSvMoE3qtBJkyiswdVaWkm8YTK"; -const char *RewardsNormaladdr = "RMgye9jeczNjQx9Uzq8no8pTLiCSwuHwkz"; char RewardsCChexstr[67] = { "03da60379d924c2c30ac290d2a86c2ead128cb7bd571f69211cb95356e2dcc5eb9" }; uint8_t RewardsCCpriv[32] = { 0x82, 0xf5, 0xd2, 0xe7, 0xd6, 0x99, 0x33, 0x77, 0xfb, 0x80, 0x00, 0x97, 0x23, 0x3d, 0x1e, 0x6f, 0x61, 0xa9, 0xb5, 0x2e, 0x5e, 0xb4, 0x96, 0x6f, 0xbc, 0xed, 0x6b, 0xe2, 0xbb, 0x7b, 0x4b, 0xb3 }; #include "CCcustom.inc" @@ -94,8 +89,6 @@ uint8_t RewardsCCpriv[32] = { 0x82, 0xf5, 0xd2, 0xe7, 0xd6, 0x99, 0x33, 0x77, 0x // Dice #define FUNCNAME IsDiceInput #define EVALCODE EVAL_DICE -const char *DiceCCaddr = "REabWB7KjFN5C3LFMZ5odExHPenYzHLtVw"; -const char *DiceNormaladdr = "RLEe8f7Eg3TDuXii9BmNiiiaVGraHUt25c"; char DiceCChexstr[67] = { "039d966927cfdadab3ee6c56da63c21f17ea753dde4b3dfd41487103e24b27e94e" }; uint8_t DiceCCpriv[32] = { 0x0e, 0xe8, 0xf5, 0xb4, 0x3d, 0x25, 0xcc, 0x35, 0xd1, 0xf1, 0x2f, 0x04, 0x5f, 0x01, 0x26, 0xb8, 0xd1, 0xac, 0x3a, 0x5a, 0xea, 0xe0, 0x25, 0xa2, 0x8f, 0x2a, 0x8e, 0x0e, 0xf9, 0x34, 0xfa, 0x77 }; #include "CCcustom.inc" @@ -105,8 +98,6 @@ uint8_t DiceCCpriv[32] = { 0x0e, 0xe8, 0xf5, 0xb4, 0x3d, 0x25, 0xcc, 0x35, 0xd1, // Lotto #define FUNCNAME IsLottoInput #define EVALCODE EVAL_LOTTO -const char *LottoCCaddr = "RNXZxgyWSAE6XS3qGnTaf5dVNCxnYzhPrg"; -const char *LottoNormaladdr = "RLW6hhRqBZZMBndnyPv29Yg3krh6iBYCyg"; char LottoCChexstr[67] = { "03f72d2c4db440df1e706502b09ca5fec73ffe954ea1883e4049e98da68690d98f" }; uint8_t LottoCCpriv[32] = { 0xb4, 0xac, 0xc2, 0xd9, 0x67, 0x34, 0xd7, 0x58, 0x80, 0x4e, 0x25, 0x55, 0xc0, 0x50, 0x66, 0x84, 0xbb, 0xa2, 0xe7, 0xc0, 0x39, 0x17, 0xb4, 0xc5, 0x07, 0xb7, 0x3f, 0xca, 0x07, 0xb0, 0x9a, 0xeb }; #include "CCcustom.inc" @@ -116,8 +107,6 @@ uint8_t LottoCCpriv[32] = { 0xb4, 0xac, 0xc2, 0xd9, 0x67, 0x34, 0xd7, 0x58, 0x80 // Finite State Machine #define FUNCNAME IsFSMInput #define EVALCODE EVAL_FSM -const char *FSMCCaddr = "RUKTbLBeKgHkm3Ss4hKZP3ikuLW1xx7B2x"; -const char *FSMNormaladdr = "RWSHRbxnJYLvDjpcQ2i8MekgP6h2ctTKaj"; char FSMCChexstr[67] = { "039b52d294b413b07f3643c1a28c5467901a76562d8b39a785910ae0a0f3043810" }; uint8_t FSMCCpriv[32] = { 0x11, 0xe1, 0xea, 0x3e, 0xdb, 0x36, 0xf0, 0xa8, 0xc6, 0x34, 0xe1, 0x21, 0xb8, 0x02, 0xb9, 0x4b, 0x12, 0x37, 0x8f, 0xa0, 0x86, 0x23, 0x50, 0xb2, 0x5f, 0xe4, 0xe7, 0x36, 0x0f, 0xda, 0xae, 0xfc }; #include "CCcustom.inc" @@ -127,8 +116,6 @@ uint8_t FSMCCpriv[32] = { 0x11, 0xe1, 0xea, 0x3e, 0xdb, 0x36, 0xf0, 0xa8, 0xc6, // Auction #define FUNCNAME IsAuctionInput #define EVALCODE EVAL_AUCTION -const char *AuctionCCaddr = "RL4YPX7JYG3FnvoPqWF2pn3nQknH5NWEwx"; -const char *AuctionNormaladdr = "RFtVDNmdTZBTNZdmFRbfBgJ6LitgTghikL"; char AuctionCChexstr[67] = { "037eefe050c14cb60ae65d5b2f69eaa1c9006826d729bc0957bdc3024e3ca1dbe6" }; uint8_t AuctionCCpriv[32] = { 0x8c, 0x1b, 0xb7, 0x8c, 0x02, 0xa3, 0x9d, 0x21, 0x28, 0x59, 0xf5, 0xea, 0xda, 0xec, 0x0d, 0x11, 0xcd, 0x38, 0x47, 0xac, 0x0b, 0x6f, 0x19, 0xc0, 0x24, 0x36, 0xbf, 0x1c, 0x0a, 0x06, 0x31, 0xfb }; #include "CCcustom.inc" @@ -138,8 +125,6 @@ uint8_t AuctionCCpriv[32] = { 0x8c, 0x1b, 0xb7, 0x8c, 0x02, 0xa3, 0x9d, 0x21, 0x // Heir #define FUNCNAME IsHeirInput #define EVALCODE EVAL_HEIR -const char *HeirCCaddr = "RDVHcSekmXgeYBqRupNTmqo3Rn8QRXNduy"; -const char *HeirNormaladdr = "RTPwUjKYECcGn6Y4KYChLhgaht1RSU4jwf"; char HeirCChexstr[67] = { "03c91bef3d7cc59c3a89286833a3446b29e52a5e773f738a1ad2b09785e5f4179e" }; uint8_t HeirCCpriv[32] = { 0x9d, 0xa1, 0xf8, 0xf7, 0xba, 0x0a, 0x91, 0x36, 0x89, 0x9a, 0x86, 0x30, 0x63, 0x20, 0xd7, 0xdf, 0xaa, 0x35, 0xe3, 0x99, 0x32, 0x2b, 0x63, 0xc0, 0x66, 0x9c, 0x93, 0xc4, 0x5e, 0x9d, 0xb9, 0xce }; #include "CCcustom.inc" @@ -149,8 +134,6 @@ uint8_t HeirCCpriv[32] = { 0x9d, 0xa1, 0xf8, 0xf7, 0xba, 0x0a, 0x91, 0x36, 0x89, // Channels #define FUNCNAME IsChannelsInput #define EVALCODE EVAL_CHANNELS -const char *ChannelsCCaddr = "RQy3rwX8sP9oDm3c39vGKA6H315cgtPLfr"; -const char *ChannelsNormaladdr = "RQUuT8zmkvDfXqECH4m3VD3SsHZAfnoh1v"; char ChannelsCChexstr[67] = { "035debdb19b1c98c615259339500511d6216a3ffbeb28ff5655a7ef5790a12ab0b" }; uint8_t ChannelsCCpriv[32] = { 0xec, 0x91, 0x36, 0x15, 0x2d, 0xd4, 0x48, 0x73, 0x22, 0x36, 0x4f, 0x6a, 0x34, 0x5c, 0x61, 0x0f, 0x01, 0xb4, 0x79, 0xe8, 0x1c, 0x2f, 0xa1, 0x1d, 0x4a, 0x0a, 0x21, 0x16, 0xea, 0x82, 0x84, 0x60 }; #include "CCcustom.inc" @@ -160,8 +143,8 @@ uint8_t ChannelsCCpriv[32] = { 0xec, 0x91, 0x36, 0x15, 0x2d, 0xd4, 0x48, 0x73, 0 // Oracles #define FUNCNAME IsOraclesInput #define EVALCODE EVAL_ORACLES -const char *OraclesCCaddr = "REt2C4ZMnX8YYX1DRpffNA4hECZTFm39e3"; -const char *OraclesNormaladdr = "RHkFKzn1csxA3fWzAsxsLWohoCgBbirXb5"; +//const char *OraclesCCaddr = "REt2C4ZMnX8YYX1DRpffNA4hECZTFm39e3"; +//const char *OraclesNormaladdr = "RHkFKzn1csxA3fWzAsxsLWohoCgBbirXb5"; char OraclesCChexstr[67] = { "038c1d42db6a45a57eccb8981b078fb7857b9b496293fe299d2b8d120ac5b5691a" }; uint8_t OraclesCCpriv[32] = { 0xf7, 0x4b, 0x5b, 0xa2, 0x7a, 0x5e, 0x9c, 0xda, 0x89, 0xb1, 0xcb, 0xb9, 0xe6, 0x9c, 0x2c, 0x70, 0x85, 0x37, 0xdd, 0x00, 0x7a, 0x67, 0xff, 0x7c, 0x62, 0x1b, 0xe2, 0xfb, 0x04, 0x8f, 0x85, 0xbf }; #include "CCcustom.inc" @@ -171,8 +154,6 @@ uint8_t OraclesCCpriv[32] = { 0xf7, 0x4b, 0x5b, 0xa2, 0x7a, 0x5e, 0x9c, 0xda, 0x // Prices #define FUNCNAME IsPricesInput #define EVALCODE EVAL_PRICES -const char *PricesCCaddr = "RAL5Vh8NXmFqEKJRKrk1KjKaUckK7mM1iS"; -const char *PricesNormaladdr = "RBunXCsMHk5NPd6q8SQfmpgre3x133rSwZ"; char PricesCChexstr[67] = { "039894cb054c0032e99e65e715b03799607aa91212a16648d391b6fa2cc52ed0cf" }; uint8_t PricesCCpriv[32] = { 0x0a, 0x3b, 0xe7, 0x5d, 0xce, 0x06, 0xed, 0xb7, 0xc0, 0xb1, 0xbe, 0xe8, 0x7b, 0x5a, 0xd4, 0x99, 0xb8, 0x8d, 0xde, 0xac, 0xb2, 0x7e, 0x7a, 0x52, 0x96, 0x15, 0xd2, 0xa0, 0xc6, 0xb9, 0x89, 0x61 }; #include "CCcustom.inc" @@ -182,8 +163,6 @@ uint8_t PricesCCpriv[32] = { 0x0a, 0x3b, 0xe7, 0x5d, 0xce, 0x06, 0xed, 0xb7, 0xc // Pegs #define FUNCNAME IsPegsInput #define EVALCODE EVAL_PEGS -const char *PegsCCaddr = "RHnkVb7vHuHnjEjhkCF1bS6xxLLNZPv5fd"; -const char *PegsNormaladdr = "RMcCZtX6dHf1fz3gpLQhUEMQ8cVZ6Rzaro"; char PegsCChexstr[67] = { "03c75c1de29a35e41606363b430c08be1c2dd93cf7a468229a082cc79c7b77eece" }; uint8_t PegsCCpriv[32] = { 0x52, 0x56, 0x4c, 0x78, 0x87, 0xf7, 0xa2, 0x39, 0xb0, 0x90, 0xb7, 0xb8, 0x62, 0x80, 0x0f, 0x83, 0x18, 0x9d, 0xf4, 0xf4, 0xbd, 0x28, 0x09, 0xa9, 0x9b, 0x85, 0x54, 0x16, 0x0f, 0x3f, 0xfb, 0x65 }; #include "CCcustom.inc" @@ -193,8 +172,6 @@ uint8_t PegsCCpriv[32] = { 0x52, 0x56, 0x4c, 0x78, 0x87, 0xf7, 0xa2, 0x39, 0xb0, // Marmara (reserved evalcode, the source moved to the marmara repo) #define FUNCNAME IsMarmaraInput #define EVALCODE EVAL_MARMARA -const char *MarmaraCCaddr = "RGLSRDnUqTB43bYtRtNVgmwSSd1sun2te8"; -const char *MarmaraNormaladdr = "RMN25Tn8NNzcyQDiQNuMp8UmwLMFd9thYc"; char MarmaraCChexstr[67] = { "03afc5be570d0ff419425cfcc580cc762ab82baad88c148f5b028d7db7bfeee61d" }; uint8_t MarmaraCCpriv[32] = { 0x7c, 0x0b, 0x54, 0x9b, 0x65, 0xd4, 0x89, 0x57, 0xdf, 0x05, 0xfe, 0xa2, 0x62, 0x41, 0xa9, 0x09, 0x0f, 0x2a, 0x6b, 0x11, 0x2c, 0xbe, 0xbd, 0x06, 0x31, 0x8d, 0xc0, 0xb9, 0x96, 0x76, 0x3f, 0x24 }; #include "CCcustom.inc" @@ -204,8 +181,6 @@ uint8_t MarmaraCCpriv[32] = { 0x7c, 0x0b, 0x54, 0x9b, 0x65, 0xd4, 0x89, 0x57, 0x // Payments #define FUNCNAME IsPaymentsInput #define EVALCODE EVAL_PAYMENTS -const char *PaymentsCCaddr = "REpyKi7avsVduqZ3eimncK4uKqSArLTGGK"; -const char *PaymentsNormaladdr = "RHRX8RTMAh2STWe9DHqsvJbzS7ty6aZy3d"; char PaymentsCChexstr[67] = { "0358f1764f82c63abc7c7455555fd1d3184905e30e819e97667e247e5792b46856" }; uint8_t PaymentsCCpriv[32] = { 0x03, 0xc9, 0x73, 0xc2, 0xb8, 0x30, 0x3d, 0xbd, 0xc8, 0xd9, 0xbf, 0x02, 0x49, 0xd9, 0x65, 0x61, 0x45, 0xed, 0x9e, 0x93, 0x51, 0xab, 0x8b, 0x2e, 0xe7, 0xc7, 0x40, 0xf1, 0xc4, 0xd2, 0xc0, 0x5b }; #include "CCcustom.inc" @@ -215,8 +190,7 @@ uint8_t PaymentsCCpriv[32] = { 0x03, 0xc9, 0x73, 0xc2, 0xb8, 0x30, 0x3d, 0xbd, 0 // Gateways #define FUNCNAME IsGatewaysInput #define EVALCODE EVAL_GATEWAYS -const char *GatewaysCCaddr = "RKWpoK6vTRtq5b9qrRBodLkCzeURHeEk33"; -const char *GatewaysNormaladdr = "RGJKV97ZN1wBfunuMt1tebiiHENNEq73Yh"; // wif UxJFYqEvLAjWPPRvn8NN1fRWscBxQZXZB5BBgc3HiapKVQBYNcmo +// wif UxJFYqEvLAjWPPRvn8NN1fRWscBxQZXZB5BBgc3HiapKVQBYNcmo char GatewaysCChexstr[67] = { "03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40" }; uint8_t GatewaysCCpriv[32] = { 0xf7, 0x4b, 0x5b, 0xa2, 0x7a, 0x5e, 0x9c, 0xda, 0x89, 0xb1, 0xcb, 0xb9, 0xe6, 0x9c, 0x2c, 0x70, 0x85, 0x37, 0xdd, 0x00, 0x7a, 0x67, 0xff, 0x7c, 0x62, 0x1b, 0xe2, 0xfb, 0x04, 0x8f, 0x85, 0xbf }; #include "CCcustom.inc" @@ -226,8 +200,6 @@ uint8_t GatewaysCCpriv[32] = { 0xf7, 0x4b, 0x5b, 0xa2, 0x7a, 0x5e, 0x9c, 0xda, 0 // Tokens #define FUNCNAME IsTokensInput #define EVALCODE EVAL_TOKENS -const char *TokensCCaddr = "RAMvUfoyURBRxAdVeTMHxn3giJZCFWeha2"; -const char *TokensNormaladdr = "RCNgAngYAdrfzujYyPgfbjCGNVQZzCgTad"; char TokensCChexstr[67] = { "03e6191c70c9c9a28f9fd87089b9488d0e6c02fb629df64979c9cdb6b2b4a68d95" }; uint8_t TokensCCpriv[32] = { 0x1d, 0x0d, 0x0d, 0xce, 0x2d, 0xd2, 0xe1, 0x9d, 0xf5, 0xb6, 0x26, 0xd5, 0xad, 0xa0, 0xf0, 0x0a, 0xdd, 0x7a, 0x72, 0x7d, 0x17, 0x35, 0xb5, 0xe3, 0x2c, 0x6c, 0xa9, 0xa2, 0x03, 0x16, 0x4b, 0xcf }; #include "CCcustom.inc" @@ -246,8 +218,6 @@ uint8_t CClibCCpriv[32] = { 0x57, 0xcf, 0x49, 0x71, 0x7d, 0xb4, 0x15, 0x1b, 0x4f // ImportGateway #define FUNCNAME IsImportGatewayInput #define EVALCODE EVAL_IMPORTGATEWAY -const char *ImportGatewayCCaddr = "RXJT6CRAXHFuQ2UjqdxMj7EfrayF6UJpzZ"; -const char *ImportGatewayNormaladdr = "RNFRho63Ddz1Rh2eGPETykrU4fA8r67S4Y"; char ImportGatewayCChexstr[67] = { "0397231cfe04ea32d5fafb2206773ec9fba6e15c5a4e86064468bca195f7542714" }; uint8_t ImportGatewayCCpriv[32] = { 0x65, 0xef, 0x27, 0xeb, 0x3d, 0xb0, 0xb4, 0xae, 0x0f, 0xbc, 0x77, 0xdb, 0xf8, 0x40, 0x48, 0x90, 0x52, 0x20, 0x9e, 0x45, 0x3b, 0x49, 0xd8, 0x97, 0x60, 0x8c, 0x27, 0x4c, 0x59, 0x46, 0xe1, 0xdf }; #include "CCcustom.inc" @@ -257,8 +227,6 @@ uint8_t ImportGatewayCCpriv[32] = { 0x65, 0xef, 0x27, 0xeb, 0x3d, 0xb0, 0xb4, 0x // Tokens v2 #define FUNCNAME IsTokensv2Input #define EVALCODE EVAL_TOKENSV2 -const char *Tokensv2CCaddr = "RSc4RycihBEWQP2GDvSYS46MvFJsTKaNVU"; -const char *Tokensv2Normaladdr = "RDVU97zvJamGmVBSUyTm7RcYZtxjriNGkj"; char Tokensv2CChexstr[67] = { "032fd27f72591b02f13a7f9701246eb0296b2be7cfdad32c520e594844ec3d4801" }; uint8_t Tokensv2CCpriv[32] = { 0xb5, 0xba, 0x92, 0x7f, 0x53, 0x45, 0x4f, 0xf8, 0xa4, 0xad, 0x0d, 0x38, 0x30, 0x4f, 0xd0, 0x97, 0xd1, 0xb7, 0x94, 0x1b, 0x1f, 0x52, 0xbd, 0xae, 0xa2, 0xe7, 0x49, 0x06, 0x2e, 0xd2, 0x2d, 0xa5 }; #include "CCcustom.inc" @@ -268,17 +236,15 @@ uint8_t Tokensv2CCpriv[32] = { 0xb5, 0xba, 0x92, 0x7f, 0x53, 0x45, 0x4f, 0xf8, 0 // Assets v2 #define FUNCNAME IsAssetsv2Input #define EVALCODE EVAL_ASSETSV2 -const char *Assetsv2CCaddr = "RX99NCswvrLiM6vNE4zmpKKBWMZU9zqwAk"; -const char *Assetsv2Normaladdr = "RSB4NhRbvEShUFDkZz2KACj5EEBGDtDsV9"; char Assetsv2CChexstr[67] = { "0345d2e7ab018619da6ed58ccc0138c5f58a7b754bd8e9a1a9d2b811c5fe72d467" }; uint8_t Assetsv2CCpriv[32] = { 0x46, 0x58, 0x3b, 0x18, 0xee, 0x16, 0x63, 0x51, 0x6f, 0x60, 0x6e, 0x09, 0xdf, 0x9d, 0x27, 0xc8, 0xa7, 0xa2, 0x72, 0xa5, 0xd4, 0x6a, 0x9b, 0xcb, 0xd5, 0x4f, 0x7d, 0x1c, 0xb1, 0x2e, 0x63, 0x21 }; #include "CCcustom.inc" #undef FUNCNAME #undef EVALCODE -// ParamNFT -#define FUNCNAME IsNFTDataInput -#define EVALCODE EVAL_NFTDATA +// Tokel TokenData validator +#define FUNCNAME IsTokelDataInput +#define EVALCODE EVAL_TOKELDATA #include "CCcustom.inc" #undef FUNCNAME #undef EVALCODE @@ -332,118 +298,91 @@ 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(); + cp->clear(); cp->evalcode = evalcode; + bool ismixed = false; switch ( evalcode ) { case EVAL_ASSETS: - strcpy(cp->unspendableCCaddr,AssetsCCaddr); - strcpy(cp->normaladdr,AssetsNormaladdr); strcpy(cp->CChexstr,AssetsCChexstr); memcpy(cp->CCpriv,AssetsCCpriv,32); cp->validate = AssetsValidate; cp->ismyvin = IsAssetsInput; break; case EVAL_FAUCET: - strcpy(cp->unspendableCCaddr,FaucetCCaddr); - strcpy(cp->normaladdr,FaucetNormaladdr); strcpy(cp->CChexstr,FaucetCChexstr); memcpy(cp->CCpriv,FaucetCCpriv,32); cp->validate = FaucetValidate; cp->ismyvin = IsFaucetInput; break; case EVAL_REWARDS: - strcpy(cp->unspendableCCaddr,RewardsCCaddr); - strcpy(cp->normaladdr,RewardsNormaladdr); strcpy(cp->CChexstr,RewardsCChexstr); memcpy(cp->CCpriv,RewardsCCpriv,32); cp->validate = RewardsValidate; cp->ismyvin = IsRewardsInput; break; case EVAL_DICE: - strcpy(cp->unspendableCCaddr,DiceCCaddr); - strcpy(cp->normaladdr,DiceNormaladdr); strcpy(cp->CChexstr,DiceCChexstr); memcpy(cp->CCpriv,DiceCCpriv,32); cp->validate = DiceValidate; cp->ismyvin = IsDiceInput; break; case EVAL_LOTTO: - strcpy(cp->unspendableCCaddr,LottoCCaddr); - strcpy(cp->normaladdr,LottoNormaladdr); strcpy(cp->CChexstr,LottoCChexstr); memcpy(cp->CCpriv,LottoCCpriv,32); cp->validate = LottoValidate; cp->ismyvin = IsLottoInput; break; case EVAL_FSM: - strcpy(cp->unspendableCCaddr,FSMCCaddr); - strcpy(cp->normaladdr,FSMNormaladdr); strcpy(cp->CChexstr,FSMCChexstr); memcpy(cp->CCpriv,FSMCCpriv,32); cp->validate = FSMValidate; cp->ismyvin = IsFSMInput; break; case EVAL_AUCTION: - strcpy(cp->unspendableCCaddr,AuctionCCaddr); - strcpy(cp->normaladdr,AuctionNormaladdr); strcpy(cp->CChexstr,AuctionCChexstr); memcpy(cp->CCpriv,AuctionCCpriv,32); cp->validate = AuctionValidate; cp->ismyvin = IsAuctionInput; break; case EVAL_HEIR: - strcpy(cp->unspendableCCaddr,HeirCCaddr); - strcpy(cp->normaladdr,HeirNormaladdr); strcpy(cp->CChexstr,HeirCChexstr); memcpy(cp->CCpriv,HeirCCpriv,32); cp->validate = HeirValidate; cp->ismyvin = IsHeirInput; break; case EVAL_CHANNELS: - strcpy(cp->unspendableCCaddr,ChannelsCCaddr); - strcpy(cp->normaladdr,ChannelsNormaladdr); strcpy(cp->CChexstr,ChannelsCChexstr); memcpy(cp->CCpriv,ChannelsCCpriv,32); cp->validate = ChannelsValidate; cp->ismyvin = IsChannelsInput; break; case EVAL_ORACLES: - strcpy(cp->unspendableCCaddr,OraclesCCaddr); - strcpy(cp->normaladdr,OraclesNormaladdr); strcpy(cp->CChexstr,OraclesCChexstr); memcpy(cp->CCpriv,OraclesCCpriv,32); cp->validate = OraclesValidate; cp->ismyvin = IsOraclesInput; break; case EVAL_PRICES: - strcpy(cp->unspendableCCaddr,PricesCCaddr); - strcpy(cp->normaladdr,PricesNormaladdr); strcpy(cp->CChexstr,PricesCChexstr); memcpy(cp->CCpriv,PricesCCpriv,32); cp->validate = PricesValidate; cp->ismyvin = IsPricesInput; break; case EVAL_PEGS: - strcpy(cp->unspendableCCaddr,PegsCCaddr); - strcpy(cp->normaladdr,PegsNormaladdr); strcpy(cp->CChexstr,PegsCChexstr); memcpy(cp->CCpriv,PegsCCpriv,32); cp->validate = PegsValidate; cp->ismyvin = IsPegsInput; break; case EVAL_PAYMENTS: - strcpy(cp->unspendableCCaddr,PaymentsCCaddr); - strcpy(cp->normaladdr,PaymentsNormaladdr); strcpy(cp->CChexstr,PaymentsCChexstr); memcpy(cp->CCpriv,PaymentsCCpriv,32); cp->validate = PaymentsValidate; cp->ismyvin = IsPaymentsInput; break; case EVAL_GATEWAYS: - strcpy(cp->unspendableCCaddr,GatewaysCCaddr); - strcpy(cp->normaladdr,GatewaysNormaladdr); strcpy(cp->CChexstr,GatewaysCChexstr); memcpy(cp->CCpriv,GatewaysCCpriv,32); cp->validate = GatewaysValidate; @@ -451,16 +390,12 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) break; case EVAL_TOKENS: - strcpy(cp->unspendableCCaddr, TokensCCaddr); - strcpy(cp->normaladdr, TokensNormaladdr); strcpy(cp->CChexstr, TokensCChexstr); memcpy(cp->CCpriv, TokensCCpriv, 32); cp->validate = TokensValidate; cp->ismyvin = IsTokensInput; break; case EVAL_IMPORTGATEWAY: - strcpy(cp->unspendableCCaddr, ImportGatewayCCaddr); - strcpy(cp->normaladdr, ImportGatewayNormaladdr); strcpy(cp->CChexstr, ImportGatewayCChexstr); memcpy(cp->CCpriv, ImportGatewayCCpriv, 32); cp->validate = ImportGatewayValidate; @@ -468,32 +403,36 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) break; case EVAL_TOKENSV2: - strcpy(cp->unspendableCCaddr, Tokensv2CCaddr); - strcpy(cp->normaladdr, Tokensv2Normaladdr); strcpy(cp->CChexstr, Tokensv2CChexstr); memcpy(cp->CCpriv, Tokensv2CCpriv, 32); cp->validate = Tokensv2Validate; cp->ismyvin = IsTokensv2Input; + ismixed = true; break; case EVAL_ASSETSV2: - strcpy(cp->unspendableCCaddr,Assetsv2CCaddr); - strcpy(cp->normaladdr,Assetsv2Normaladdr); strcpy(cp->CChexstr,Assetsv2CChexstr); memcpy(cp->CCpriv,Assetsv2CCpriv,32); cp->validate = Assetsv2Validate; cp->ismyvin = IsAssetsv2Input; + ismixed = true; break; - case EVAL_NFTDATA: - cp->validate = NFTDataValidate; - cp->ismyvin = IsNFTDataInput; + case EVAL_TOKELDATA: + cp->validate = TokelDataValidate; + cp->ismyvin = IsTokelDataInput; + ismixed = true; break; default: - if ( CClib_initcp(cp,evalcode) < 0 ) + if (CClib_initcp(cp, evalcode) < 0) return(0); break; } + // init addresses now here: + if (cp->CChexstr[0]) { + _GetCCaddress(cp->unspendableCCaddr, cp->evalcode, pubkey2pk(ParseHex(cp->CChexstr)), ismixed); + Getscriptaddress(cp->normaladdr, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG); + } return(cp); } diff --git a/src/cc/CCfaucet.h b/src/cc/CCfaucet.h index af3237ab1b5..7155168380e 100644 --- a/src/cc/CCfaucet.h +++ b/src/cc/CCfaucet.h @@ -29,4 +29,6 @@ UniValue FaucetFund(const CPubKey& mypk,uint64_t txfee,int64_t funds); UniValue FaucetGet(const CPubKey& mypk,uint64_t txfee); UniValue FaucetInfo(); +int64_t AddFaucetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs); + #endif diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 035543ecb1b..faae94b78a5 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -74,8 +74,13 @@ Details. #include "../init.h" #include "../unspentccindex.h" #include "rpc/server.h" +#include "CCupgrades.h" +// invalid burn pubkey stop using it #define CC_BURNPUBKEY "02deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead" //!< 'dead' pubkey in hex for burning tokens (if tokens are sent to it, they become 'burned') +// valid burn pubkey, start support in June 2022 +#define CC_BURNPUBKEY_FIXED "02deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaa" + /// \cond INTERNAL #define CC_MAXVINS 1024 #define CC_REQUIREMENTS_MSG (KOMODO_NSPV_SUPERLITE?"to use CC contracts you need to nspv_login first\n":"to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n") @@ -87,6 +92,8 @@ Details. #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 ASSETS_NORMAL_DUST 500 + #define MAY2020_NNELECTION_HARDFORK 1592146800 //(Sunday, June 14th, 2020 03:00:00 PM UTC) #define JUNE2021_NNELECTION_HARDFORK 1623682800 // dPoW Season 5 Monday, June 14th, 2021 (03:00:00 PM UTC) @@ -230,8 +237,9 @@ struct CCVintxProbe { 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 - //!< or vouts with two evalcodes: EVAL_TOKENS, evalcodeNFT. + // unused: + // uint8_t evalcodeAdd; //!< additional eval code for spending from three-eval-code vouts with EVAL_TOKENS, cc evalcode, cc evalcode NFT + //!< or vouts with two evalcodes: EVAL_TOKENS, evalcodeAdd. //!< Set by AddTokenCCInputs function char unspendableCCaddr[64]; //!< global contract cryptocondition address, set by CCinit function @@ -288,10 +296,10 @@ struct CCcontract_info std::vector< struct CCVintxProbe > CCvintxprobes; // &txs,uint8_t evalcode,uint8_t funcid); /// \endcond -/// \cond INTERNAL -#define IGUANA_READ 0 -#define IGUANA_WRITE 1 -int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); -int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *endianedp); -/// \endcond CScript GetScriptForMultisig(int nRequired, const std::vector& keys); @@ -618,8 +615,6 @@ CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue,CPubKey pk, std::vector>* vData = NULL); -bool CCtoAnon(const CC *cond); - CTxOut MakeCC1voutMixed(uint8_t evalcode, CAmount nValue, CPubKey pk, std::vector *vData = NULL); CTxOut MakeCC1of2voutMixed(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector *vData = NULL); @@ -866,11 +861,14 @@ CPubKey check_signing_pubkey(CScript scriptSig); bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey); extern std::vector NULL_pubkeys; //!< constant value for use in functions where such value might be passed @see FinalizeCCTx -#define FINALIZECCTX_NO_CHANGE 0x1 + +#define FINALIZECCTX_ALWAYS_CHANGE 0x0 +#define FINALIZECCTX_NO_CHANGE 0x1 #define FINALIZECCTX_NO_CHANGE_WHEN_ZERO 0x2 +#define FINALIZECCTX_NO_CHANGE_WHEN_DUST 0x4 /// overload old-style FinalizeCCTx for compatibility -/// @param skipmask parameter is not used +/// @param changeFlag whether or not add normal change. Default 0 is always add change even 0 /// @param cp contract info structure with cc eval code, module's global address and privkey. It also could have a vector of probe cryptoconditions created by CCAddVintxCond /// @param mtx prepared transaction to sign /// @param mypk my pubkey to sign @@ -878,7 +876,7 @@ extern std::vector NULL_pubkeys; //!< constant value for use in functio /// @param opret opreturn vout which function will add if it is not empty /// @param pubkeys array of pubkeys to make multiple probe 1of2 cc's with the call Make1of2cond(cp->evalcode, globalpk, pubkeys[i]) /// @returns signed transaction in hex encoding -std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret,std::vector pubkeys = NULL_pubkeys); +std::string FinalizeCCTx(uint32_t changeFlag, struct CCcontract_info* cp, CMutableTransaction& mtx, CPubKey mypk, CAmount txfee, CScript opret, std::vector pubkeys = NULL_pubkeys); /// FinalizeCCTx is a very useful function that will properly sign both CC and normal inputs, adds normal change and might add an opreturn output. /// This allows for Antara module transaction creation rpc functions to create an CMutableTransaction object, add the appropriate vins and vouts to it and use FinalizeCCTx to properly sign the transaction. @@ -890,7 +888,7 @@ std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTr /// For a set of 1of2 cc with pairs of module globalpk and some other pk, spending with global pk, pass a vector with pubkeys as 'pubkeys' parameter. /// For any other cc case use CCAddVintxCond function to add any possible probe cryptoconditions to the cp opbject. /// @param remote true if the caller is in remote nspv mode -/// @param skipmask parameter is not used +/// @param changeFlag whether or not add normal change. Default 0 is always add change even 0 /// @param cp contract info structure with cc eval code, module's global address and privkey. It also could have a vector of probe cryptoconditions created by CCAddVintxCond /// @param mtx prepared transaction to sign /// @param mypk my pubkey to sign @@ -898,11 +896,11 @@ std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTr /// @param opret opreturn vout which function will add if it is not empty /// @param pubkeys array of pubkeys to make multiple probe 1of2 cc's with the call Make1of2cond(cp->evalcode, globalpk, pubkeys[i]) /// @returns signed transaction in hex encoding -UniValue FinalizeCCTxExt(bool remote, uint64_t skipmask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret, std::vector pubkeys = NULL_pubkeys); +UniValue FinalizeCCTxExt(bool remote, uint32_t changeFlag, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, CAmount txfee, CScript opret, std::vector pubkeys = NULL_pubkeys); /// version FinalizeCCTx for CC v2, for params @see FinalizeCCTxExt /// @returns signed transaction in hex and optional PartiallySigned univalue object with vin indexes and partially signed conditions (if signature threshold not reached) -UniValue FinalizeCCV2Tx(bool remote, uint64_t mask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret); +UniValue FinalizeCCV2Tx(bool remote, uint32_t changeFlag, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, CAmount txfee, CScript opret); /// Add signature to multisig scriptSig /// @param mtx mutable tx with multisig cc vins @@ -928,26 +926,23 @@ void SetCCunspentsWithMempool(std::vector > &unspentOutputs, const char *coinaddr, uint256 creationId); - -/// SetCCunspents returns a vector of unspent outputs for a cc address -/// @param[out] unspentOutputs vector of pairs of objects CAddressUnspentCCKey and CAddressUnspentCCValue -/// @param coinaddr cc address where unspent outputs are searched -void SetCCunspentsCCIndex(std::vector > &unspentOutputs, const char *coinaddr); +void SetCCunspentsCCIndex(std::vector > &unspentOutputs, const char *coinaddr, uint256 creationId = uint256()); /// Adds mempool outputs to a vector of unspent outputs for a cc address /// @param[out] unspentOutputs vector of pairs of objects CAddressUnspentCCKey and CAddressUnspentCCValue /// @param coinaddr cc address where unspent outputs are searched -/// @param creationId txid of cc instance creation tx, might be empty to return all txns on coinaddr -void AddCCunspentsCCIndexMempool(std::vector > &unspentOutputs, const char *coinaddr, uint256 creationId); +/// @param creationId txid of cc instance creation tx, can be empty to return all txns on coinaddr +void AddCCunspentsCCIndexMempool(std::vector > &unspentOutputs, const char *coinaddr, uint256 creationId = uint256()); -/// SetCCtxids returns a vector of all outputs on an address +/// SetAddressIndexOutputs searches address index for a vector of outputs on an address /// @param[out] addressIndex vector of pairs of address index key and amount /// @param coinaddr address where the unspent outputs are searched /// @param CCflag if true the function searches for cc outputs, otherwise for normal outputs -void SetCCtxids(std::vector > &addressIndex,char *coinaddr,bool CCflag = true); +/// @param beginHeight if beginHeight and endHeight positive the function searches for cc outputs starting from this height +/// @param endHeight if beginHeight and endHeight positive the function searches for cc outputs till this height +void SetAddressIndexOutputs(std::vector>& addressIndex, char* coinaddr, bool ccflag, int32_t beginHeight = 0, int32_t endHeight = 0); -/// overloaded SetCCtxids returns a vector of filtered txids which have outputs on an address +/// SetCCtxids searches address index for a vector of filtered txids which have outputs on an address /// @param[out] txids returned vector of txids /// @param coinaddr address where the unspent outputs are searched /// @param ccflag if true the function searches for cc outputs, otherwise for normal outputs @@ -974,7 +969,7 @@ int64_t NSPV_AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total /// @returns amount of added normal inputs or amount of all normal inputs in the wallet /// @see AddNormalinputsLocal /// @see AddNormalinputsRemote -int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs, bool remote=false); +CAmount AddNormalinputs(CMutableTransaction &mtx, CPubKey mypk, CAmount total, int32_t maxinputs, bool remote=false); /// Local version for cc runnnig on the same node, adds normal (not cc) inputs to the transaction object vin array for the specified total amount using available utxos in the wallet, to fund the transaction /// @param mtx mutable transaction object @@ -982,7 +977,7 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3 /// @param total amount of inputs to add. If total equals to 0 the function does not add inputs but returns amount of all available normal inputs in the wallet /// @param maxinputs maximum number of inputs to add /// @returns amount of added normal inputs or amount of all normal inputs in the wallet -int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs); +CAmount AddNormalinputsLocal(CMutableTransaction &mtx, CPubKey mypk, CAmount total,int32_t maxinputs); /// AddNormalinputs2 adds normal (not cc) inputs to the transaction object vin array for the specified total amount using utxos on my pubkey's TX_PUBKEY address (my pubkey is set by -pubkey command line parameter), to fund the transaction. /// 'My pubkey' is the -pubkey parameter of komodod. @@ -990,7 +985,7 @@ int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total /// @param total amount of inputs to add. If total equals to 0 the function does not add inputs but returns amount of all available normal inputs in the wallet /// @param maxinputs maximum number of inputs to add /// @returns amount of added normal inputs or amount of all normal inputs on my pubkey's address -int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinputs); +CAmount AddNormalinputs2(CMutableTransaction &mtx, CAmount total, int32_t maxinputs); /// Remote version, does not use local wallet, adds normal (not cc) inputs to the transaction object vin array for the specified total amount using available utxos on mypk, to fund the transaction /// @param mtx mutable transaction object @@ -998,7 +993,7 @@ int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinput /// @param total amount of inputs to add. If total equals to 0 the function does not add inputs but returns amount of all available normal inputs in the wallet /// @param maxinputs maximum number of inputs to add /// @returns amount of added normal inputs or amount of all normal inputs in the wallet -int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t total, int32_t maxinputs, bool mempool = false); +CAmount AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, CAmount total, int32_t maxinputs, bool mempool = false); /// CCutxovalue returns amount of an utxo. The function does this without loading the utxo transaction, by using address index only /// @param coinaddr address where the utxo is searched @@ -1009,7 +1004,7 @@ int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t to int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout,int32_t CCflag); /// @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); +int32_t CC_vinselect(int32_t *aboveip, CAmount *abovep, int32_t *belowip, CAmount *belowp, struct CC_utxo utxos[], int32_t numunspents, CAmount value); /// @private void CCAddVintxCond(struct CCcontract_info *cp, const CCwrapper &condWrapped, const uint8_t *priv = NULL); @@ -1027,21 +1022,11 @@ int32_t oracleprice_add(std::vector &publishers,CPubKey UniValue OracleFormat(uint8_t *data,int32_t datalen,char *format,int32_t formatlen); /// @private -bool GetCCDropAsOpret(const CScript &scriptPubKey, CScript &opret); +CScript GetCCDropAsOpret(const CScript &scriptPubKey); /*! \cond INTERNAL */ -// curve25519 and sha256 -bits256 curve25519_shared(bits256 privkey,bits256 otherpub); -bits256 curve25519_basepoint9(); -bits256 curve25519(bits256 mysecret,bits256 basepoint); -void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len); -bits256 bits256_doublesha256(char *deprecated,uint8_t *data,int32_t datalen); -// UniValue ValueFromAmount(const CAmount& amount); // defined in server.h -/*! \endcond */ - -/*! \cond INTERNAL */ -int64_t TotalPubkeyNormalInputs(const CTransaction &tx, const CPubKey &pubkey); -int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey); +CAmount TotalPubkeyNormalInputs(Eval *eval, const CTransaction &tx, const CPubKey &pubkey); +CAmount TotalPubkeyCCInputs(Eval *eval, 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 */ @@ -1083,6 +1068,16 @@ inline UniValue MakeResultSuccess(const std::string &txhex) { } /*! \endcond */ +/// returns burn pubkey list +/// @param height current height at which some new pubkeys are activated +inline std::vector GetBurnPubKeys(int32_t nTime, int32_t nHeight) +{ + std::vector vDeadPubkeys; + vDeadPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY))); + if (CCUpgrades::IsUpgradeActive(nTime, nHeight, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)) + vDeadPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY_FIXED))); // activate new burn pubkey + return vDeadPubkeys; +} /// @private bool inline IS_REMOTE(const CPubKey &remotepk) { return remotepk.IsValid(); } @@ -1093,10 +1088,24 @@ void AddSigData2UniValue(UniValue &result, int32_t vini, UniValue& ccjson, std:: /// 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); +int32_t ensure_CCrequirements(uint8_t evalcode, bool isRemote = false); + +/// returns true if tx is in active chain +/// @param txid txid of tx to check +bool IsTxidInActiveChain(uint256 txid); + +/// returns true if block hash is in active chain +/// @param hashBlock hash of block to check +bool IsBlockHashInActiveChain(uint256 hashBlock); + +/// subcalls an additional evalcode validator +bool SubcallCCValidate(Eval* eval, uint8_t evalcode, const CTransaction& ctx, int32_t nIn); extern bool fUnspentCCIndex; // if unspent cc index enabled +/// decode condition to UniValue for decoderawtransaction +UniValue CCDecodeMixedMode(const CC *cond); + /// @private forward decl struct CLockedInMemoryUtxos; @@ -1193,7 +1202,7 @@ void CCLogPrintStream(const char *category, int level, const char *functionName, stream << (category ? category : "") << " "; if (functionName != NULL) - stream << functionName << " "; + stream << functionName << "() "; if (level < 0) stream << "ERROR:" << " "; print_to_stream(stream); @@ -1217,6 +1226,13 @@ void CCLogPrintStream(const char *category, int level, const char *functionName, /// @see LOGSTREAM #define LOGSTREAMFN(category, level, logoperator) CCLogPrintStream( category, level, __func__, [&](std::ostringstream &stream) {logoperator;} ) +extern struct CCcontract_info CCinfos[]; +extern std::string MYCCLIBNAME; +bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx,unsigned int nIn); + +/// class CCERROR to replace old single threaded CCerror global var +/// CCERROR is instanciated as thread_local and allows safely set error in cc rpc calls +/// Note: we cannot use std::string as the 'message' member as it would fail in thread_local mode class CCERROR { private: void init() { diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index 13986bcef4f..bb6bbe61dd3 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2022 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -14,7 +14,6 @@ ******************************************************************************/ #include "CCtokens.h" -#include "old/CCtokens_v0.h" ///#include "importcoin.h" /* TODO: correct this: @@ -22,21 +21,14 @@ */ #include "CCtokens_impl.h" +#include "CCTokelData.h" + thread_local uint32_t tokenValIndentSize = 0; // for debug logging // helper funcs: -bool IsEqualScriptPubKeys(const CScript &spk1, const CScript &spk2) -{ - char addr1[KOMODO_ADDRESS_BUFSIZE]; - char addr2[KOMODO_ADDRESS_BUFSIZE]; - Getscriptaddress(addr1, spk1); - Getscriptaddress(addr2, spk2); - return strcmp(addr1, addr2) == 0; -} - // remove token->unspendablePk (it is only for marker usage) static void FilterOutTokensUnspendablePk(const std::vector &sourcePubkeys, std::vector &destPubkeys) { struct CCcontract_info *cpTokens, tokensC; @@ -50,16 +42,6 @@ static void FilterOutTokensUnspendablePk(const std::vector &sourcePubke } -static bool HasMyCCVin(struct CCcontract_info *cp, const CTransaction &tx) -{ - for (auto const &vin : tx.vin) { - if (cp->ismyvin(vin.scriptSig)) { - return true; - } - } - return false; -} - static std::vector GetEvalCodesCCV2(const CScript& spk) { std::vector ccdata = spk.GetCCV2SPK(); @@ -84,17 +66,18 @@ static std::vector GetEvalCodesCCV2(const CScript& spk) // internal function to check if token vout is valid // returns amount or -1 // return also tokenid -CAmount TokensV1::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) +CAmount TokensV1::CheckTokensvout(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, CScript &opret, uint256 &reftokenid, uint8_t &funcId, std::string &errorStr) { // this is just for log messages indentation fur debugging recursive calls: std::string indentStr = std::string().append(tokenValIndentSize, '.'); + const char *funcname = __func__; - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "CheckTokensvout()" << " entered for txid=" << tx.GetHash().GetHex() << " v=" << v << std::endl); + LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << funcname << "()" << " entered for txid=" << tx.GetHash().GetHex() << " v=" << v << 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 << "CheckTokensvout()" << " incorrect params: (n == 0 or v < 0 or v >= n)" << " v=" << v << " n=" << n << " returning error" << std::endl); + LOGSTREAM(cctokens_log, CCLOG_INFO, stream << indentStr << funcname << "()" << " incorrect params: (n == 0 or v < 0 or v >= n)" << " v=" << v << " n=" << n << " returning error" << std::endl); errorStr = "out of bounds"; return(-1); } @@ -123,17 +106,10 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used // 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; - } - } - + std::for_each (tx.vin.begin(), tx.vin.end(), [&](const CTxIn &vin){ cp->ismyvin(vin.scriptSig) ? hasMyccvin = true : hasMyccvin = hasMyccvin; }); - CScript opret; bool isLastVoutOpret; - if (GetCCDropAsOpret(tx.vout[v].scriptPubKey, opret)) + if (!(opret = GetCCDropAsOpret(tx.vout[v].scriptPubKey)).empty()) { isLastVoutOpret = false; } @@ -144,11 +120,11 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used } uint256 tokenIdOpret; - std::vector oprets; + std::vector vdatas; 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); + funcId = DecodeTokenOpRetV1(opret, tokenIdOpret, voutPubkeysInOpret, vdatas); if (funcId == 0) { // bad opreturn // errorStr = "can't decode opreturn data"; @@ -162,6 +138,13 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used errorStr = "tokenbase tx cannot have cc vins"; return -1; } + + // call extra data validators + for (auto const &vd : vdatas) + if (vd.size() > 0 && vd[0] != 0 && vd[0] != cp->evalcode) // vd[0] is additional evalcode to validate tokendata + if (!SubcallCCValidate(eval, vd[0], tx, 0)) + return -1; + // set returned tokend to tokenbase txid: reftokenid = tx.GetHash(); } @@ -178,29 +161,17 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used 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: - TokenDataTuple tokenData; - vscript_t vopretNonfungible; - GetTokenData(reftokenid, tokenData, vopretNonfungible); - - if (vopretNonfungible.size() > 0) { - // shift evalcodes so the first is NFT evalcode - evalCode2 = evalCode1; - evalCode1 = vopretNonfungible[0]; + if (vdatas.size() >= 1) { + evalCode1 = vdatas[0].size() > 0 ? vdatas[0][0] : 0; + if (vdatas.size() >= 2) + evalCode2 = vdatas[1].size() > 0 ? vdatas[1][0] : 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); + LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << funcname << "()" << " for txid=" << tx.GetHash().GetHex() << " checking evalCode1=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " voutPubkeysInOpret.size()=" << voutPubkeysInOpret.size() << std::endl); if (IsTokenTransferFuncid(funcId)) { @@ -216,15 +187,15 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used if (voutPubkeysInOpret.size() == 1) { if (evalCode1 == 0 && evalCode2 == 0) { - if (IsEqualScriptPubKeys(tx.vout[v].scriptPubKey, MakeTokensCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeysInOpret[0]).scriptPubKey)) + if (IsEqualDestinations(tx.vout[v].scriptPubKey, MakeTokensCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeysInOpret[0]).scriptPubKey)) return tx.vout[v].nValue; } else if (evalCode1 != 0 && evalCode2 == 0) { - if (IsEqualScriptPubKeys(tx.vout[v].scriptPubKey, MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, voutPubkeysInOpret[0]).scriptPubKey)) + if (IsEqualDestinations(tx.vout[v].scriptPubKey, MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, voutPubkeysInOpret[0]).scriptPubKey)) return tx.vout[v].nValue; } else if (evalCode1 != 0 && evalCode2 != 0) { - if (IsEqualScriptPubKeys(tx.vout[v].scriptPubKey, MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeysInOpret[0]).scriptPubKey)) + if (IsEqualDestinations(tx.vout[v].scriptPubKey, MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeysInOpret[0]).scriptPubKey)) return tx.vout[v].nValue; } else { @@ -235,15 +206,15 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used else if (voutPubkeysInOpret.size() == 2) { if (evalCode1 == 0 && evalCode2 == 0) { - if (IsEqualScriptPubKeys(tx.vout[v].scriptPubKey, MakeTokensCC1of2vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeysInOpret[0], voutPubkeysInOpret[1]).scriptPubKey)) + if (IsEqualDestinations(tx.vout[v].scriptPubKey, MakeTokensCC1of2vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeysInOpret[0], voutPubkeysInOpret[1]).scriptPubKey)) return tx.vout[v].nValue; } else if (evalCode1 != 0 && evalCode2 == 0) { - if (IsEqualScriptPubKeys(tx.vout[v].scriptPubKey, MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeysInOpret[0], voutPubkeysInOpret[1]).scriptPubKey)) + if (IsEqualDestinations(tx.vout[v].scriptPubKey, MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeysInOpret[0], voutPubkeysInOpret[1]).scriptPubKey)) return tx.vout[v].nValue; } else if (evalCode1 != 0 && evalCode2 != 0) { - if (IsEqualScriptPubKeys(tx.vout[v].scriptPubKey, MakeTokensCC1of2vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeysInOpret[0], voutPubkeysInOpret[1]).scriptPubKey)) + if (IsEqualDestinations(tx.vout[v].scriptPubKey, MakeTokensCC1of2vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeysInOpret[0], voutPubkeysInOpret[1]).scriptPubKey)) return tx.vout[v].nValue; } else { @@ -262,7 +233,7 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used // funcid == 'c' if (tx.IsCoinImport()) { // imported coin is checked in EvalImportCoin - if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker + if (IsTokenMarkerVout(tx.vout[v]) > 0LL) // exclude marker return tx.vout[v].nValue; else return 0; @@ -270,39 +241,39 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used vscript_t vorigPubkey; std::string dummyName, dummyDescription; - std::vector oprets; + std::vector vdatas; - 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 (DecodeTokenCreateOpRetV1(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, vdatas) == 0) { + LOGSTREAM(cctokens_log, CCLOG_INFO, stream << indentStr << funcname << "()" << " 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); + GetOpReturnCCBlob(vdatas, vopretNFT); // calc cc outputs for origPubkey CAmount 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 (IsEqualScriptPubKeys(vout.scriptPubKey, testvout.scriptPubKey)) + if (IsEqualDestinations(vout.scriptPubKey, testvout.scriptPubKey)) ccOutputs += vout.nValue; } - CAmount normalInputs = TotalPubkeyNormalInputs(tx, origPubkey); // calc normal inputs really signed by originator pubkey (someone not cheating with originator pubkey) + CAmount normalInputs = TotalPubkeyNormalInputs(eval, 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); + LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << funcname << "()" << " 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 (IsEqualScriptPubKeys(tx.vout[v].scriptPubKey, testvout.scriptPubKey)) // check vout sent to orig pubkey + if (IsEqualDestinations(tx.vout[v].scriptPubKey, testvout.scriptPubKey)) // 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); + LOGSTREAM(cctokens_log, CCLOG_INFO, stream << indentStr << funcname << "()" << " 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; } @@ -321,44 +292,36 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used 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); + LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << funcname << "()" << " function ValidateTokenOpret returned not-null funcId=" << (char)(funcId ? funcId : ' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); - TokenDataTuple tokenData; - vscript_t vopretExtra, vopretNonfungible; + vscript_t vopretExtra; // 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 // 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); + std::vector vdatas; + DecodeTokenOpRetV1(tx.vout.back().scriptPubKey, tokenIdOpret, voutPubkeysInOpret, vdatas); + LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << funcname << "()" << " vdatas.size()=" << vdatas.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); + if (vdatas.size() > 0) + vopretExtra = vdatas[0]; + LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << funcname << "()" << " vopretExtra=" << HexStr(vopretExtra) << std::endl); - // get non-fungible data - GetTokenData(reftokenid, tokenData, 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: // non-fungible-eval -> EVAL_TOKENS -> assets-eval - - if (vopretNonfungible.size() > 0) - evalCodeNonfungible = evalCode1 = vopretNonfungible.begin()[0]; if (vopretExtra.size() > 0) - evalCode2 = vopretExtra.begin()[0]; + evalCode2 = vopretExtra[0]; if (evalCode1 == EVAL_TOKENS && evalCode2 != 0) { evalCode1 = evalCode2; // for using MakeTokensCC1vout(evalcode,...) instead of MakeCC1vout(EVAL_TOKENS, evalcode...) @@ -392,8 +355,7 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used } // 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]"))); + 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 ) @@ -403,8 +365,7 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used // 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]"))); + 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) @@ -436,8 +397,7 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used 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(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) @@ -447,8 +407,8 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used // try all test vouts: for (const 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); + if (IsEqualDestinations(t.first.scriptPubKey, tx.vout[v].scriptPubKey)) { // test vout matches + LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << funcname << "()" << " 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; } } @@ -460,18 +420,38 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used { vscript_t vorigPubkey; std::string dummyName, dummyDescription; - std::vector oprets; + std::vector vdatas; uint8_t version; - 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 (DecodeTokenCreateOpRetV1(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, vdatas) == 0) { + LOGSTREAM(cctokens_log, CCLOG_INFO, stream << indentStr << funcname << "()" << " 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); + GetOpReturnCCBlob(vdatas, vopretNFT); + if (origPubkey == GetUnspendable(cp, NULL)) { + errorStr = "cannot create tokens with token shared pubkey"; + return -1; + } + + /* + if (vdatas.size() > 1) { + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " invalid vdata size for txid=" << tx.GetHash().GetHex() << " v=" << v << " isLastVoutOpret=" << isLastVoutOpret << std::endl); + return -1; // could not be more than 1 vdata in opreturn + } + std::string tdataError; + std::cerr << __func__ << " vdatas.size()=" << vdatas.size() << std::endl; + if (vdatas.size() > 0) + std::cerr << __func__ << " vdatas[0]=" << HexStr(vdatas[0]) << std::endl; + if (vdatas.size() > 0 && !CheckTokelData(vdatas[0], tdataError)) { + errorStr = tdataError; + return -1; // invalid tokendata + } + */ + // TODO: add voutPubkeys for 'c' tx /* this would not work for imported tokens: @@ -489,38 +469,38 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used CAmount 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 (IsEqualScriptPubKeys(vout.scriptPubKey, testvout.scriptPubKey)) + CTxOut testvout = MakeCC1vout(EVAL_TOKENS, vout.nValue, origPubkey); + if (IsEqualDestinations(vout.scriptPubKey, testvout.scriptPubKey)) ccOutputs += vout.nValue; } - CAmount 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); + CAmount origInputs = TotalPubkeyNormalInputs(eval, 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 << funcname << "()" << " origInputs=" << origInputs << " 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); + if (origInputs >= ccOutputs) { + LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << funcname << "()" << " assured origInputs >= 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); + CTxOut testvout = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, origPubkey); - if (IsEqualScriptPubKeys(tx.vout[v].scriptPubKey, testvout.scriptPubKey)) // check vout sent to orig pubkey + if (IsEqualDestinations(tx.vout[v].scriptPubKey, testvout.scriptPubKey)) // check vout sent to orig pubkey 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_log, CCLOG_INFO, stream << indentStr << funcname << "()" << " skipping vout not fulfilled normalInputs >= ccOutput" << " for tokenbase=" << reftokenid.GetHex() << " origInputs=" << origInputs << " ccOutputs=" << ccOutputs << std::endl); } } else { // imported tokens are checked in the eval::ImportCoin() validation code - if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker + if (IsTokenMarkerVout(tx.vout[v]) > 0LL) // exclude marker return tx.vout[v].nValue; 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_log, CCLOG_DEBUG1, stream << indentStr << funcname << "()" << " no valid vouts evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); } } return(0); // normal vout @@ -531,29 +511,26 @@ CAmount TokensV1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used // internal function to check if token 2 vout is valid // returns amount or -1 // return also tokenid -CAmount TokensV2::CheckTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 &reftokenid, std::string &errorStr) +CAmount TokensV2::CheckTokensvout(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, CScript &opret, uint256 &reftokenid, uint8_t &funcId, 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 << "CheckTokensvout()" << " entered for txid=" << tx.GetHash().GetHex() << " v=" << v << std::endl); + // this is just for log messages indentation fur debugging recursive calls: + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG2, stream << " entered for txid=" << tx.GetHash().GetHex() << " v=" << v << 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 << "CheckTokensvout()" << " incorrect params: (n == 0 or v < 0 or v >= n)" << " v=" << v << " n=" << n << " returning error" << std::endl); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " incorrect params: (n == 0 or v < 0 or v >= n)" << " v=" << v << " n=" << n << " returning error" << std::endl); errorStr = "out of bounds"; return -1; } - std::vector vParams; CScript dummy; - if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams)) - { - CScript opret; + if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && + tx.vout[v].scriptPubKey.SpkHasEvalcodeCCV2(EVAL_TOKENSV2)) // it's token output, check it + { bool isLastVoutOpret; - if (GetCCDropAsOpret(tx.vout[v].scriptPubKey, opret)) + if (!(opret = GetCCDropAsOpret(tx.vout[v].scriptPubKey)).empty()) { isLastVoutOpret = false; } @@ -564,20 +541,37 @@ CAmount TokensV2::CheckTokensvout(bool goDeeper, bool checkPubkeys, struct CCcon } uint256 tokenIdOpret; - std::vector oprets; + std::vector vdatas; std::vector vpksdummy; // token opret most important checks (tokenid == reftokenid, tokenid is non-zero, tx is 'tokenbase'): - uint8_t funcId = TokensV2::DecodeTokenOpRet(opret, tokenIdOpret, vpksdummy, oprets); + funcId = TokensV2::DecodeTokenOpRet(opret, tokenIdOpret, vpksdummy, vdatas); if (funcId == 0) { // bad opreturn - // errorStr = "can't decode opreturn data"; - // return -1; - return 0; // not token vout, skip + errorStr = "can't decode opreturn data"; + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " found token vout with non-token opret for txid=" << tx.GetHash().GetHex() << " v=" << v << " isLastVoutOpret=" << isLastVoutOpret << std::endl); + return -1; // not token vout, skip } // basic checks: if (IsTokenCreateFuncid(funcId)) { + /* + if (vdatas.size() > 1) { + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " invalid vdata size for txid=" << tx.GetHash().GetHex() << " v=" << v << " isLastVoutOpret=" << isLastVoutOpret << std::endl); + return -1; // could not be more than 1 vdata in opreturn + } + std::string tdataError; + if (vdatas.size() > 0 && !CheckTokelData(vdatas[0], tdataError)) { + errorStr = tdataError; + return -1; // invalid tokendata + } + */ + // call extra data validators + for (auto const &vd : vdatas) + if (vd.size() > 0 && vd[0] != 0 && vd[0] != cp->evalcode) // vd[0] is additional evalcode to validate tokendata + if (!SubcallCCValidate(eval, vd[0], tx, 0)) + return -1; + // set returned tokend to tokenbase txid: reftokenid = tx.GetHash(); } @@ -596,15 +590,20 @@ CAmount TokensV2::CheckTokensvout(bool goDeeper, bool checkPubkeys, struct CCcon } // skip token marker (token global address) - if (IsTokenMarkerVout(tx.vout[v])) + CAmount markerAmount = IsTokenMarkerVout(tx.vout[v]); + if (markerAmount > 0) { + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " skipping marker vout for txid=" << tx.GetHash().GetHex() << " v=" << v << std::endl); return 0; + } + else if (markerAmount < 0) { + errorStr = "invalid marker value"; + return -1; + } - - if (tx.vout[v].scriptPubKey.SpkHasEvalcodeCCV2(EVAL_TOKENSV2)) // it's token output, check it - { - /* let's do not verify opdrop in V2 + /* let's do not verify opdrop data in V2 it is a just hint to users and does not really affect vaidation: - // get output pubkeys and verify vout + // get output pubkeys and verify vout + { if (vParams.size() == 0) { errorStr = "no opdrop data"; return -1; @@ -623,60 +622,32 @@ CAmount TokensV2::CheckTokensvout(bool goDeeper, bool checkPubkeys, struct CCcon std::vector evalcodes = GetEvalCodesCCV2(tx.vout[v].scriptPubKey); testVout = MakeTokensCCMofNvoutMixed(evalcodes.size() >= 1 ? evalcodes[0] : 0, evalcodes.size() >= 2 ? evalcodes[1] : 0, tx.vout[v].nValue, opdrop.m, opdrop.vKeys); - if (!IsEqualScriptPubKeys(testVout.scriptPubKey, tx.vout[v].scriptPubKey)) { + if (!IsEqualDestinations(testVout.scriptPubKey, tx.vout[v].scriptPubKey)) { errorStr = "pubkeys in opdrop don't match vout"; return -1; } - */ - - return tx.vout[v].nValue; - } + } */ + return tx.vout[v].nValue; } return(0); // normal or non-token2 vout } - -/*static 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; - - 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); - } - } - return CPubKey(); //return invalid pubkey -}*/ - // old token tx validation entry point // 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"); + if (std::count_if(tx.vout.begin(), tx.vout.end(), [](const CTxOut &v){ vuint8_t vd; return GetOpReturnData(v.scriptPubKey, vd); }) > 1) + return eval->Invalid("only one opreturn supported"); + 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()) + if (!eval->state.IsValid()) return false; //TokenExactAmounts has already called eval->Invalid() else return eval->Invalid(errorStr); @@ -687,135 +658,29 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & static bool report_validation_error(const std::string &func, Eval* eval, const CTransaction &tx, const std::string &errorStr) { LOGSTREAM(cctokens_log, CCLOG_ERROR, stream << func << " validation error: " << errorStr << " tx=" << HexStr(E_MARSHAL(ss << tx)) << std::endl); - return eval->Invalid(errorStr); -} - -// checking creation txns is available with cryptocondition v2 mixed mode -// therefore do not forget to check that the creation tx does not have cc inputs! -static bool CheckTokensV2CreateTx(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx) -{ - std::vector vpksdummy; - std::vector oprets; - vuint8_t vorigpk; - std::string name, description; - uint256 tokenid; - - // check it is a create tx - int32_t createNum = 0; - int32_t transferNum = 0; - for(int32_t i = 0; i < tx.vout.size(); i ++) - { - if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition()) - { - CScript opdrop; - if (GetCCDropAsOpret(tx.vout[i].scriptPubKey, opdrop)) - { - uint8_t funcid = TokensV2::DecodeTokenOpRet(opdrop, tokenid, vpksdummy, oprets); - if (IsTokenCreateFuncid(funcid)) { - createNum ++; - if (createNum > 1) - return report_validation_error(__func__, eval, tx, "can't have more than 1 create vout"); - - TokensV2::DecodeTokenCreateOpRet(opdrop, vorigpk, name, description, oprets); - - // check this is really creator - CPubKey origpk = pubkey2pk(vorigpk); - if (TotalPubkeyNormalInputs(tx, origpk) == 0) - return report_validation_error(__func__, eval, tx, "no vins with creator pubkey"); - } - else if(IsTokenTransferFuncid(funcid)) - transferNum ++; - } - } - } - - if (createNum > 0 && transferNum > 0) - return report_validation_error(__func__, eval, tx, "can't have both create and transfer vouts"); - - if (createNum == 0 && transferNum == 0) - { - // if no OP_DROP vouts check the last vout opreturn: - if (IsTokenCreateFuncid(TokensV2::DecodeTokenOpRet(tx.vout.back().scriptPubKey, tokenid, vpksdummy, oprets))) - { - TokensV2::DecodeTokenCreateOpRet(tx.vout.back().scriptPubKey, vorigpk, name, description, oprets); - - // check this is really creator - CPubKey origpk = pubkey2pk(vorigpk); - if (TotalPubkeyNormalInputs(tx, origpk) == 0) - return report_validation_error(__func__, eval, tx, "no vins with creator pubkey"); - createNum ++; - } - } - - // check that creation tx does not have my cc vins - if (createNum > 0) { - if (HasMyCCVin(cp, tx)) - return report_validation_error(__func__, eval, tx, "creation tx can't have token vins"); - return true; - } - return false; + return !eval->state.IsValid() ? false/*error state already set*/ : eval->Invalid(errorStr) /* set error state and exit*/; } // token 2 cc validation entry point bool Tokensv2Validate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) { - if (!TokensIsVer1Active(eval)) - return eval->Invalid("tokens v2 not active yet"); - // check boundaries: if (tx.vout.size() < 1) return report_validation_error(__func__, eval, tx, "no vouts"); - std::string errorStr; - - if (CheckTokensV2CreateTx(cp, eval, tx)) //found create tx and it is valid - return true; - if (eval->state.IsInvalid()) // create tx is invalid - return false; + if (std::count_if(tx.vout.begin(), tx.vout.end(), [](const CTxOut &v){ vuint8_t vd; return GetOpReturnData(v.scriptPubKey, vd); }) > 1) + return eval->Invalid("only one opreturn supported"); - // check 't' vouts (could have multiple tokenids) + std::string errorStr; + // check token vouts (token txns could have multiple tokenids for multiple token transfer) if (!TokensExactAmounts(true, cp, eval, tx, errorStr)) return report_validation_error(__func__, eval, tx, errorStr); - return true; -} - + // disable many markers: -// default old version functions: - -/*void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible) -{ - GetNonfungibleData(tokenid, vopretNonfungible); + return true; } -std::string CreateTokenLocal(CAmount txfee, CAmount tokensupply, std::string name, std::string description, vscript_t nonfungibleData) -{ - return CreateTokenLocal(txfee, tokensupply, name, description, nonfungibleData); -}*/ - -/*bool IsTokenMarkerVout(CTxOut vout) { - return IsTokenMarkerVout(vout); // TODO: fix CheckMigration for tokens V2 -}*/ - -//CAmount IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true*/, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid) -//{ -// return IsTokensvout(goDeeper, checkPubkeys, cp, eval, tx, v, reftokenid); -//} - -//CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const char *tokenaddr, uint256 tokenid, CAmount total, int32_t maxinputs, bool useMempool) -//{ -// return AddTokenCCInputs(cp, mtx, tokenaddr, tokenid, total, maxinputs, useMempool); -//} -//CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const CPubKey &pk, uint256 tokenid, CAmount total, int32_t maxinputs, bool useMempool) -//{ -// return AddTokenCCInputs(cp, mtx, pk, tokenid, total, maxinputs, useMempool); -//} - -//CAmount GetTokenBalance(CPubKey pk, uint256 tokenid, bool usemempool) -//{ -// return GetTokenBalance(pk, tokenid, usemempool); -//} -//UniValue TokenInfo(uint256 tokenid) { return TokenInfo(tokenid); } UniValue TokenList() { @@ -832,24 +697,30 @@ UniValue TokenList() std::vector origpubkey; std::string name, description; - if (myGetTransaction(txid, vintx, hashBlock) != 0) { - std::vector oprets; - if (vintx.vout.size() > 0 && DecodeTokenCreateOpRetV1(vintx.vout.back().scriptPubKey, origpubkey, name, description, oprets) != 0) { - result.push_back(txid.GetHex()); - } - else { - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "DecodeTokenCreateOpRetV1 failed for txid=" << txid.GetHex() < oprets; + if (vintx.vout.size() > 0 && DecodeTokenCreateOpRetV1(vintx.vout.back().scriptPubKey, origpubkey, name, description, oprets) != 0) { + result.push_back(txid.GetHex()); + } + else { + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "DecodeTokenCreateOpRetV1 failed for txid=" << txid.GetHex() <normaladdr, false, cp->evalcode, 0, zeroid, 'c'); // find by old normal addr marker + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "cp->normaladdr=" << cp->normaladdr << " SetCCtxids txids.size()=" << txids.size() << std::endl); for (std::vector::const_iterator it = txids.begin(); it != txids.end(); it++) { addTokenId(*it); } SetCCunspents(unspentOutputs, cp->unspendableCCaddr, true); // find by burnable validated cc addr marker - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " unspentOutputs.size()=" << unspentOutputs.size() << std::endl); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "cp->unspendableCCaddr=" << cp->unspendableCCaddr << " SetCCunspents unspentOutputs.size()=" << unspentOutputs.size() << std::endl); for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { addTokenId(it->first.txhash); } @@ -857,62 +728,162 @@ UniValue TokenList() return(result); } -/*bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, std::string &errorStr) -{ - return TokensExactAmounts(goDeeper, cp, eval, tx, errorStr); -}*/ - -// some v2 wrappers for template functions - -/*std::string CreateTokenLocal2(CAmount txfee, CAmount tokensupply, std::string name, std::string description, vscript_t nonfungibleData) -{ - return CreateTokenLocal(txfee, tokensupply, name, description, nonfungibleData); -}*/ - -UniValue TokenV2List() +UniValue TokenV2List(const UniValue ¶ms) { UniValue result(UniValue::VARR); + const bool CC_OUTPUTS_TRUE = true; + + int32_t beginHeight = 0; + int32_t endHeight = 0; + CPubKey checkPK; + std::string checkAddr; + if (params.exists("beginHeight")) + beginHeight = atoi(params["beginHeight"].getValStr().c_str()); + if (params.exists("endHeight")) + endHeight = atoi(params["endHeight"].getValStr().c_str()); + if (params.exists("pubkey")) + checkPK = pubkey2pk(ParseHex(params["pubkey"].getValStr().c_str())); + if (params.exists("address")) + checkAddr = params["address"].getValStr(); struct CCcontract_info *cp, C; cp = CCinit(&C, EVAL_TOKENSV2); auto addTokenId = [&](uint256 tokenid, const CScript &opreturn) { - std::vector origpubkey; + vscript_t origpubkey; std::string name, description; std::vector oprets; if (DecodeTokenCreateOpRetV2(opreturn, origpubkey, name, description, oprets) != 0) { - result.push_back(tokenid.GetHex()); + if (checkPK.IsValid()) { + if (checkPK == pubkey2pk(origpubkey)) + result.push_back(tokenid.GetHex()); + } + else if (!checkAddr.empty()) { + char origaddr[KOMODO_ADDRESS_BUFSIZE]; + Getscriptaddress(origaddr, TokensV2::MakeCC1vout(EVAL_TOKENSV2, 0LL, pubkey2pk(origpubkey)).scriptPubKey); + if (checkAddr == origaddr) + result.push_back(tokenid.GetHex()); + } + else + result.push_back(tokenid.GetHex()); } else { LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "DecodeTokenCreateOpRetV2 failed for tokenid=" << tokenid.GetHex() << " opreturn.size=" << opreturn.size() << std::endl); } }; - if (fUnspentCCIndex) - { - std::vector > unspentOutputs; - - SetCCunspentsCCIndex(unspentOutputs, cp->unspendableCCaddr, zeroid); // find by burnable validated cc addr marker - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "unspentOutputs.size()=" << unspentOutputs.size() << std::endl); - for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { - addTokenId(it->first.creationid, it->second.opreturn); + if (beginHeight > 0 || endHeight > 0) { + if (endHeight <= 0) { + LOCK(cs_main); + endHeight = chainActive.Height(); } + std::vector> addressIndexOutputs; + SetAddressIndexOutputs(addressIndexOutputs, cp->unspendableCCaddr, CC_OUTPUTS_TRUE, beginHeight, endHeight); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "SetAddressIndexOutputs addressIndexOutputs.size()=" << addressIndexOutputs.size() << std::endl); + for (const auto &it : addressIndexOutputs) { + CTransaction creationtx; + uint256 hashBlock; + if (!it.first.spending && + myGetTransaction(it.first.txhash, creationtx, hashBlock) && creationtx.vout.size() > 0) + { + bool isBlockHashInActiveChain = false; + { + LOCK(cs_main); + isBlockHashInActiveChain = IsBlockHashInActiveChain(hashBlock); + } + if (isBlockHashInActiveChain) + addTokenId(it.first.txhash, creationtx.vout.back().scriptPubKey); + } + } } else { - bool CC_INPUTS_TRUE = true; - std::vector > unspentOutputs; - - SetCCunspents(unspentOutputs, cp->unspendableCCaddr, CC_INPUTS_TRUE); - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "unspentOutputs.size()=" << unspentOutputs.size() << std::endl); - for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { - CTransaction creationtx; - uint256 hashBlock; - if (myGetTransaction(it->first.txhash, creationtx, hashBlock) && creationtx.vout.size() > 0) - addTokenId(it->first.txhash, creationtx.vout.back().scriptPubKey); + if (fUnspentCCIndex) + { + std::vector > unspentOutputs; + + SetCCunspentsCCIndex(unspentOutputs, cp->unspendableCCaddr, zeroid); // find by burnable validated cc addr marker + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " cp->unspendableCCaddr=" << cp->unspendableCCaddr << " SetCCunspentsCCIndex unspentOutputs.size()=" << unspentOutputs.size() << std::endl); + for (const auto &it : unspentOutputs) { + bool isTxidInActiveChain = false; + { + LOCK(cs_main); + isTxidInActiveChain = IsTxidInActiveChain(it.first.creationid); + } + if (isTxidInActiveChain) + addTokenId(it.first.creationid, it.second.opreturn); + } + } + else + { + std::vector > unspentOutputs; + + SetCCunspents(unspentOutputs, cp->unspendableCCaddr, CC_OUTPUTS_TRUE); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " cp->unspendableCCaddr=" << cp->unspendableCCaddr << " SetCCunspents unspentOutputs.size()=" << unspentOutputs.size() << std::endl); + for (const auto &it : unspentOutputs) { + CTransaction creationtx; + uint256 hashBlock; + if (myGetTransaction(it.first.txhash, creationtx, hashBlock) && creationtx.vout.size() > 0) + { + bool isBlockHashInActiveChain = false; + { + LOCK(cs_main); + isBlockHashInActiveChain = IsBlockHashInActiveChain(hashBlock); + } + if (isBlockHashInActiveChain) + addTokenId(it.first.txhash, creationtx.vout.back().scriptPubKey); + } + } } } return(result); +} + +// get token indexkey for pubkey for old tokens: +std::vector GetTokenV1IndexKeys(const CPubKey &pk) +{ + std::vector tokenindexkeys; + char tokenindexkeyPK[KOMODO_ADDRESS_BUFSIZE]; + struct CCcontract_info *cp, C; + cp = CCinit(&C, EVAL_TOKENS); + + GetTokensCCaddress(cp, tokenindexkeyPK, pk, false); + tokenindexkeys.push_back(tokenindexkeyPK); + + return tokenindexkeys; +} + +// get conds from pubkey: +std::vector GetTokenV2Conds(const CPubKey &pk) +{ + std::vector tokenconds; + char normaladdr[KOMODO_ADDRESS_BUFSIZE]; + Getscriptaddress(normaladdr, CScript() << vuint8_t(pk.begin(), pk.end()) << OP_CHECKSIG); + std::vector dests{ CTxDestination(pk), DecodeDestination(normaladdr) }; + +// for (CC_SUBVER ccSubVersion = CC_MIXED_MODE_SUBVER_0; i < dests.size() && ccSubVersion <= CC_MIXED_MODE_SUBVER_MAX; ccSubVersion = (CC_SUBVER)(ccSubVersion + 1), i ++) { + for (int i = 0; i < dests.size(); i ++) { + CCwrapper tokenCond( MakeTokensv2CCcondMofNDest(EVAL_TOKENSV2, 0, 1, { dests[i] }) ); + //if (!CCtoAnon(tokenCond.get())) { std::cerr << __func__ << " CCtoAnon failed" << std::endl; continue; } // now in CCPubKey() + if (tokenCond == nullptr) continue; + tokenconds.push_back(tokenCond); + } + return tokenconds; +} + +// get all token indexkeys for pubkey for tokens mixed mode: +std::vector GetTokenV2IndexKeys(const CPubKey &pk) +{ + std::vector tokenindexkeys; + + std::vector tokenconds = GetTokenV2Conds(pk); + for (auto const &cond : tokenconds) { + char tokenindexkey[KOMODO_ADDRESS_BUFSIZE]; + CC_SUBVER ccSubVersion = (cc_typeMask(cond.get()) & (1 << CC_Secp256k1hash)) ? CC_MIXED_MODE_SECHASH_SUBVER_1 : CC_MIXED_MODE_SUBVER_0; + Getscriptaddress(tokenindexkey, CCPubKey(cond.get(), ccSubVersion)); + tokenindexkeys.push_back(tokenindexkey); + } + return tokenindexkeys; } \ No newline at end of file diff --git a/src/cc/CCtokens.h b/src/cc/CCtokens.h index 701f838c4e3..09a2231889e 100644 --- a/src/cc/CCtokens.h +++ b/src/cc/CCtokens.h @@ -23,24 +23,19 @@ #include #include "CCinclude.h" +const CAmount TOKENS_MARKER_VALUE = 10000; +const uint8_t TOKENS_OPRETURN_VERSION = 1; +const int TOKENS_MAX_NAME_LENGTH = 32; +const int TOKENS_MAX_DESC_LENGTH = 4096; + // implementation of basic token functions -/// 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); // see template impl typedef std::tuple TokenDataTuple; // pubkey, name, desc // CCcustom bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); bool Tokensv2Validate(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); - -// wrappers for tokens cc v1 or v2 -////std::string CreateTokenLocal(CAmount txfee, CAmount tokensupply, std::string name, std::string description, vscript_t nonfungibleData); -//bool IsTokenMarkerVout(CTxOut vout); - /// 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 @@ -50,7 +45,7 @@ bool Tokensv2Validate(struct CCcontract_info *cp,Eval* eval,const CTransaction & /// @param maxinputs maximum number of inputs to add. If 0 then CC_MAXVINS define is used //CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const CPubKey &pk, uint256 tokenid, CAmount total, int32_t maxinputs, bool useMempool = false); -/// Adds token inputs to transaction object. If tokenid is a non-fungible token then the function will set evalCodeNFT variable in the cp object to the eval code from tokencreate tx NFT data to spend NFT outputs properly +/// Adds token inputs to transaction object. /// @param cp CCcontract_info structure /// @param mtx mutable transaction object /// @param tokenaddr address where token inputs to add @@ -60,7 +55,7 @@ bool Tokensv2Validate(struct CCcontract_info *cp,Eval* eval,const CTransaction & /// @see AddTokenCCInputs //CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const char *tokenaddr, uint256 tokenid, CAmount total, int32_t maxinputs, bool useMempool = false); -/// Adds token inputs to transaction object. If tokenid is a non-fungible token then the function will set evalCodeNFT variable in the cp object to the eval code from tokencreate tx NFT data to spend NFT outputs properly +/// Adds token inputs to transaction object. /// @param cp CCcontract_info structure /// @param mtx mutable transaction object /// @param pk pubkey from which the token cc address will be created and token inputs are added @@ -74,17 +69,13 @@ bool Tokensv2Validate(struct CCcontract_info *cp,Eval* eval,const CTransaction & /// 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 -//CAmount IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid); - -template CAmount IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid); +template CAmount IsTokensvout(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 @@ -215,6 +206,10 @@ CC *MakeTokensv2CCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPu /// @returns cryptocondition object. Must be disposed with cc_free function when not used any more CC *MakeTokensv2CCcondMofN(uint8_t evalcode1, uint8_t evalcode2, uint8_t M, std::vector pks); +/// same for CTxDestination which may be either CPubKey or CKeyID +CC *MakeTokensv2CCcondMofNDest(uint8_t evalcode1, uint8_t evalcode2, uint8_t M, std::vector dests); + + /// 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_TOKENSV2 and evalcode parameter value). /// The returned output should be added to a transaction vout array. @@ -281,15 +276,23 @@ CTxOut MakeTokensCC1of2voutMixed(uint8_t evalcode, uint8_t evalcode2, CAmount nV /// @see CCcontract_info CTxOut MakeTokensCCMofNvoutMixed(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, uint8_t M, const std::vector & pks, vscript_t* pvData = nullptr); +/// same as @see MakeTokensCCMofNvoutMixed but for CPubKeys or CKeyIDs +CTxOut MakeTokensCCMofNDestVoutMixed(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, uint8_t M, const std::vector &dests, vscript_t* pvData); + UniValue TokenList(); -UniValue TokenV2List(); +UniValue TokenV2List(const UniValue ¶ms); + +/// @private +std::vector GetTokenV1IndexKeys(const CPubKey &pk); +/// @private +std::vector GetTokenV2IndexKeys(const CPubKey &pk); +/// @private +std::vector GetTokenV2Conds(const CPubKey &pk); inline bool IsTokenCreateFuncid(uint8_t funcid) { return funcid == 'c'; } inline bool IsTokenTransferFuncid(uint8_t funcid) { return funcid == 't'; } -bool IsEqualScriptPubKeys(const CScript &spk1, const CScript &spk2); - -bool TokensIsVer1Active(const Eval *eval); +bool IsEqualDestinations(const CScript &spk1, const CScript &spk2); const char cctokens_log[] = "cctokens"; @@ -299,7 +302,6 @@ class TokensV1 { static uint8_t EvalCode() { return EVAL_TOKENS; } static bool IsMixed() { return false; } - static CScript EncodeTokenCreateOpRet(const std::vector &origpubkey, const std::string &name, const std::string &description, const std::vector &oprets) { return ::EncodeTokenCreateOpRetV1(origpubkey, name, description, oprets); @@ -318,7 +320,7 @@ class TokensV1 { return ::DecodeTokenOpRetV1(scriptPubKey, tokenid, voutPubkeys, oprets); } - static CAmount 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); + static CAmount CheckTokensvout(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, CScript &opret, uint256 &reftokenid, uint8_t &funcId, std::string &errorStr); // conds: static CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) @@ -339,23 +341,27 @@ class TokensV1 { } // vouts: - static CTxOut MakeCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk, std::vector>* vData = NULL) + static CTxOut MakeCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk, std::vector* vData = NULL) { return ::MakeCC1vout(evalcode, nValue, pk, vData); } - static CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk, std::vector>* vData = NULL) + static CTxOut MakeCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector* vData = NULL) + { + return ::MakeCC1of2vout(evalcode, nValue, pk1, pk2, vData); + } + static CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk, std::vector* vData = NULL) { return ::MakeTokensCC1vout(evalcode, nValue, pk, vData); } - static CTxOut MakeTokensCC1vout(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, CPubKey pk, std::vector>* vData = NULL) + static CTxOut MakeTokensCC1vout(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, CPubKey pk, std::vector* vData = NULL) { return ::MakeTokensCC1vout(evalcode1, evalcode2, nValue, pk, vData); } - static CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector>* vData = NULL) + static CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector* vData = NULL) { return ::MakeTokensCC1of2vout(evalcode, nValue, pk1, pk2, vData); } - static CTxOut MakeTokensCC1of2vout(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector>* vData = NULL) + static CTxOut MakeTokensCC1of2vout(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector* vData = NULL) { return ::MakeTokensCC1of2vout(evalcode1, evalcode2, nValue, pk1, pk2, vData); } @@ -368,11 +374,25 @@ class TokensV1 { return ::MakeTokensCC1vout(evalcode1, evalcode2, nValue, pks[0], pvvData); else return CTxOut(); - } + } - static UniValue FinalizeCCTx(bool remote, uint64_t CCmask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret) + static CTxOut MakeTokensCCMofNDestVout(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, uint8_t M, const std::vector &dests, std::vector* pvvData = NULL) { - return ::FinalizeCCTxExt(remote, CCmask, cp, mtx, mypk, txfee, opret); + // convert to pubkey (no other dest support in cc v1) + std::vector pks; + for (auto const &dest : dests) + if (dest.which() == TX_PUBKEY) + pks.push_back(boost::get(dest)); + return MakeTokensCCMofNvout(evalcode1, evalcode2, nValue, M, pks, pvvData); + } + + static UniValue FinalizeCCTx(bool remote, uint32_t changeFlag, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, CAmount txfee, CScript opret) + { + return ::FinalizeCCTxExt(remote, changeFlag, cp, mtx, mypk, txfee, opret); + } + static std::vector GetTokenIndexKeys(const CPubKey &pk) + { + return ::GetTokenV1IndexKeys(pk); } }; @@ -401,7 +421,7 @@ class TokensV2 { return ::DecodeTokenOpRetV2(scriptPubKey, tokenid, oprets); } - static CAmount 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); + static CAmount CheckTokensvout(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, CScript &opret, uint256 &reftokenid, uint8_t &funcId, std::string &errorStr); // conds: static CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) @@ -426,6 +446,10 @@ class TokensV2 { { return ::MakeCC1voutMixed(evalcode, nValue, pk, (pvvData != nullptr ? &(*pvvData)[0] : nullptr)); } + static CTxOut MakeCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2, std::vector* pvvData = NULL) + { + return ::MakeCC1of2voutMixed(evalcode, nValue, pk1, pk2, (pvvData != nullptr ? &(*pvvData)[0] : nullptr)); + } static CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk, std::vector* pvvData = NULL) { return ::MakeTokensCC1voutMixed(evalcode, nValue, pk, (pvvData != nullptr ? &(*pvvData)[0] : nullptr)); @@ -447,18 +471,28 @@ class TokensV2 { { return ::MakeTokensCCMofNvoutMixed(evalcode1, evalcode2, nValue, M, pks, (pvvData != nullptr ? &(*pvvData)[0] : nullptr)); } - static UniValue FinalizeCCTx(bool remote, uint64_t CCmask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret) + static CTxOut MakeTokensCCMofNDestVout(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, uint8_t M, const std::vector &dests, std::vector* pvvData = NULL) + { + return ::MakeTokensCCMofNDestVoutMixed(evalcode1, evalcode2, nValue, M, dests, (pvvData != nullptr ? &(*pvvData)[0] : nullptr)); + } + + static UniValue FinalizeCCTx(bool remote, uint32_t changeFlag, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, CAmount txfee, CScript opret) + { + return ::FinalizeCCV2Tx(remote, changeFlag, cp, mtx, mypk, txfee, opret); + } + + static std::vector GetTokenIndexKeys(const CPubKey &pk) { - return ::FinalizeCCV2Tx(remote, FINALIZECCTX_NO_CHANGE_WHEN_ZERO, cp, mtx, mypk, txfee, opret); + return ::GetTokenV2IndexKeys(pk); } }; /// @private -template uint8_t ValidateTokenOpret(uint256 txid, const CScript &scriptPubKey, uint256 tokenid); +//template uint8_t ValidateTokenOpret(uint256 txid, const CScript &scriptPubKey, uint256 tokenid); /// @private template bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector &vinPubkeys); /// @private -template bool IsTokenMarkerVout(CTxOut vout); +template CAmount IsTokenMarkerVout(CTxOut vout); /// @private uint8_t DecodeTokenOpretVersion(const CScript &scriptPubKey); diff --git a/src/cc/CCtokens_impl.h b/src/cc/CCtokens_impl.h index 21fe00f66c0..dee742aca2e 100644 --- a/src/cc/CCtokens_impl.h +++ b/src/cc/CCtokens_impl.h @@ -18,6 +18,8 @@ // templates for either tokens or tokens2 functions' implementation +#include "key_io.h" + #include "CCtokens.h" #include "CCassets.h" #include "CCassetsCore_impl.h" @@ -26,15 +28,14 @@ // get non-fungible data from 'tokenbase' tx (the data might be empty) template -bool GetTokenData(uint256 tokenid, TokenDataTuple &tokenData, vscript_t &vopretNonfungible) +bool GetTokenData(Eval *eval, uint256 tokenid, TokenDataTuple &tokenData, vscript_t &vextraData) { CTransaction tokenbasetx; uint256 hashBlock; tokenData = std::make_tuple(vuint8_t(), std::string(), std::string()); - vopretNonfungible.clear(); - if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) { + if (!GetTxUnconfirmedOpt(eval, tokenid, tokenbasetx, hashBlock)) { LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "could not load token creation tx=" << tokenid.GetHex() << std::endl); return false; } @@ -43,12 +44,12 @@ bool GetTokenData(uint256 tokenid, TokenDataTuple &tokenData, vscript_t &vopretN if (tokenbasetx.vout.size() > 0) { std::vector origpubkey; std::string name, description; - std::vector oprets; + std::vector vdatas; uint8_t funcid; - if (IsTokenCreateFuncid(V::DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets))) { - if (oprets.size() > 0) - vopretNonfungible = oprets[0]; + if (IsTokenCreateFuncid(V::DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, vdatas))) { + if (vdatas.size() > 0) + vextraData = vdatas[0]; tokenData = std::make_tuple(origpubkey, name, description); return true; } @@ -57,7 +58,7 @@ bool GetTokenData(uint256 tokenid, TokenDataTuple &tokenData, vscript_t &vopretN } template -uint8_t GetTokenOpReturnVersion(uint256 tokenid) +uint8_t GetTokenOpReturnVersion(Eval *eval, uint256 tokenid) { CTransaction tokencreatetx; uint256 hashBlock; @@ -65,7 +66,7 @@ uint8_t GetTokenOpReturnVersion(uint256 tokenid) std::string name, desc; std::vector oprets; - if (myGetTransaction(tokenid, tokencreatetx, hashBlock) && + if (GetTxUnconfirmedOpt(eval, tokenid, tokencreatetx, hashBlock) && tokencreatetx.vout.size() > 0 && V::DecodeTokenCreateOpRet(tokencreatetx.vout.back().scriptPubKey, vorigpk, name, desc, oprets) != 0) return DecodeTokenOpretVersion(tokencreatetx.vout.back().scriptPubKey); @@ -81,19 +82,10 @@ CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c CAmount totalinputs = 0; int32_t n = 0; const bool CC_INPUTS_TRUE = true; + const char *funcname = __func__; if (cp->evalcode != V::EvalCode()) - 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 - TokenDataTuple tokenData; - vscript_t vopretNonfungible; - GetTokenData(tokenid, tokenData, vopretNonfungible); - if (vopretNonfungible.size() > 0) - cp->evalcodeNFT = vopretNonfungible.begin()[0]; // set evalcode of NFT, for signing - } + LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << funcname << "()" << " warning: EVAL_TOKENS *cp is needed but used evalcode=" << (int)cp->evalcode << std::endl); // make lambda to use it for either index kind: auto add_token_vin = [&](uint256 txhash, int32_t index, CAmount satoshis) -> void @@ -119,24 +111,28 @@ CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c strcmp(destaddr, cp->unspendableaddr2) != 0*/) // or the logic is to allow to spend all available tokens (what about unspendableaddr3)? return; - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "checked tx vout destaddress=" << destaddr << " amount=" << tx.vout[index].nValue << std::endl); + LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << funcname << "()" << " checking tx vout destaddress=" << destaddr << " amount=" << tx.vout[index].nValue << std::endl); - if (IsTokensvout(true, true, cp, NULL, tx, index, tokenid) > 0 && !myIsutxo_spentinmempool(ignoretxid, ignorevin, txhash, index)) + if (IsTokensvout(cp, NULL, tx, index, tokenid) > 0 && !myIsutxo_spentinmempool(ignoretxid, ignorevin, txhash, index)) { if (total != 0 && maxinputs != 0) // if it is not just to calc amount... mtx.vin.push_back(CTxIn(txhash, index, CScript())); totalinputs += satoshis; - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "adding input nValue=" << satoshis << std::endl); + LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << funcname << "()" << " adding input nValue=" << satoshis << std::endl); n++; if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) return; } + else + { + LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << funcname << "()" << " function IsTokensvout returned non-positive for txid=" << txhash.GetHex() << " index=" << index << std::endl); + } } }; // auto add_token_vin - if (fUnspentCCIndex && GetTokenOpReturnVersion(tokenid) > 0) + if (fUnspentCCIndex && GetTokenOpReturnVersion(NULL, tokenid) > 0) { std::vector > unspentOutputs; @@ -145,7 +141,7 @@ CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c AddCCunspentsCCIndexMempool(unspentOutputs, tokenaddr, tokenid); // threshold = total / (maxinputs != 0 ? maxinputs : CC_MAXVINS); // let's not use threshold - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " found unspentOutputs=" << unspentOutputs.size() << std::endl); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << funcname << "()" << " unspent ccindex found unspentOutputs=" << unspentOutputs.size() << std::endl); for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) add_token_vin(it->first.txhash, it->first.index, it->second.satoshis); } @@ -158,7 +154,7 @@ CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c else SetCCunspents(unspentOutputs, (char*)tokenaddr, CC_INPUTS_TRUE); - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " found unspentOutputs=" << unspentOutputs.size() << std::endl); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << funcname << "()" << " unspent index found unspentOutputs=" << unspentOutputs.size() << std::endl); for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) add_token_vin(it->first.txhash, it->first.index, it->second.satoshis); } @@ -170,17 +166,15 @@ CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c template CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const CPubKey &pk, uint256 tokenid, CAmount total, int32_t maxinputs, bool useMempool) { - char tokenaddr[KOMODO_ADDRESS_BUFSIZE]; - - // check if this is a NFT - TokenDataTuple tokenData; - vscript_t vopretNonfungible; - GetTokenData(tokenid, tokenData, vopretNonfungible); - if (vopretNonfungible.size() > 0) - cp->evalcodeNFT = vopretNonfungible.begin()[0]; // set evalcode of NFT - - GetTokensCCaddress(cp, tokenaddr, pk, V::IsMixed()); // GetTokensCCaddress will use 'evalcodeNFT' - return AddTokenCCInputs(cp, mtx, tokenaddr, tokenid, total, maxinputs, useMempool); + CAmount inputs = 0LL; + + std::vector tokenindexkeys = V::GetTokenIndexKeys(pk); + // get inputs from token indexkeys: + for (std::string &tokenindexkey : tokenindexkeys) { + inputs += AddTokenCCInputs(cp, mtx, tokenindexkey.c_str(), tokenid, total, maxinputs, useMempool); + if (total > 0 && inputs >= total) return inputs; // if total == 0 this just getting the balance + } + return inputs; } template @@ -192,16 +186,11 @@ UniValue TokenBeginTransferTx(CMutableTransaction &mtx, struct CCcontract_info * return MakeResultError("my pubkey not set"); } - if (V::EvalCode() == EVAL_TOKENS) { - if (!TokensIsVer1Active(NULL)) - return MakeResultError("tokens version 1 not active yet"); - } - if (txfee == 0) txfee = 10000; mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CAmount normalInputs = AddNormalinputs(mtx, mypk, txfee, 3, isRemote); + CAmount normalInputs = AddNormalinputs(mtx, mypk, txfee, 0x1000, isRemote); if (normalInputs < 0) { return MakeResultError("cannot find normal inputs"); @@ -210,16 +199,11 @@ UniValue TokenBeginTransferTx(CMutableTransaction &mtx, struct CCcontract_info * } template -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 TokenAddTransferVout(CMutableTransaction &mtx, struct CCcontract_info *cp, const CPubKey &remotepk, uint256 tokenid, const std::vector &tokenaddrs, std::vector destpubkeys, const std::vector< std::pair > &probeconds, CAmount total, bool useMempool) { - if (V::EvalCode() == EVAL_TOKENS) { - if (!TokensIsVer1Active(NULL)) - return MakeResultError("tokens version 1 not active yet"); - } - - if (amount < 0) { + if (total < 0) { CCerror = strprintf("negative amount"); - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << CCerror << "=" << amount << std::endl); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << CCerror << "=" << total << std::endl); MakeResultError("negative amount"); } @@ -230,67 +214,56 @@ UniValue TokenAddTransferVout(CMutableTransaction &mtx, struct CCcontract_info * 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_DEBUG1, stream << CCerror << std::endl); - return MakeResultError("insufficient token inputs"); - } + CAmount CCinputs = 0LL; + for (const auto &addr : tokenaddrs) { + CAmount CCinputsOne = AddTokenCCInputs(cp, mtx, addr.c_str(), tokenid, total, CC_MAXVINS, useMempool); + if (CCinputsOne > 0) + CCinputs += CCinputsOne; + if (CCinputs >= total) + break; + } - uint8_t destEvalCode = V::EvalCode(); - if (cp->evalcodeNFT != 0) // if set in AddTokenCCInputs - { - destEvalCode = cp->evalcodeNFT; - } + if (CCinputs < total) { + CCerror = strprintf("insufficient token inputs"); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << CCerror << std::endl); + return MakeResultError("insufficient token inputs"); + } - if (probecond.first != nullptr) - { - // add probe cc and kogs priv to spend from kogs global pk - CCAddVintxCond(cp, probecond.first, probecond.second); - } + for(auto const &probecond : probeconds) + CCAddVintxCond(cp, probecond.first, probecond.second); + + CScript opret = V::EncodeTokenOpRet(tokenid, destpubkeys, {}); + vscript_t vopret; + GetOpReturnData(opret, vopret); + std::vector vData { vopret }; + if (destpubkeys.size() == 1) + mtx.vout.push_back(V::MakeTokensCC1vout(V::EvalCode(), total, destpubkeys[0], &vData)); + else if (destpubkeys.size() == 2) + mtx.vout.push_back(V::MakeTokensCC1of2vout(V::EvalCode(), total, destpubkeys[0], destpubkeys[1], &vData)); + else + { + CCerror = "zero or unsupported destination pk count"; + return MakeResultError("zero or unsupported destination pubkey count"); + } - CScript opret = V::EncodeTokenOpRet(tokenid, destpubkeys, {}); + CAmount CCchange = 0L; + if (CCinputs > total) + CCchange = (CCinputs - total); + if (CCchange != 0LL) { + CScript opret = V::EncodeTokenOpRet(tokenid, {mypk}, {}); vscript_t vopret; GetOpReturnData(opret, vopret); std::vector vData { vopret }; - if (destpubkeys.size() == 1) - mtx.vout.push_back(V::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(V::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 = V::EncodeTokenOpRet(tokenid, {mypk}, {}); - vscript_t vopret; - GetOpReturnData(opret, vopret); - std::vector vData { vopret }; - mtx.vout.push_back(V::MakeTokensCC1vout(destEvalCode, CCchange, mypk, &vData)); - } - - return MakeResultSuccess(""); + mtx.vout.push_back(V::MakeTokensCC1vout(V::EvalCode(), CCchange, mypk, &vData)); } - return MakeResultError("could not find token inputs"); + + return MakeResultSuccess(""); } template UniValue TokenFinalizeTransferTx(CMutableTransaction &mtx, struct CCcontract_info *cp, const CPubKey &remotepk, CAmount txfee, const CScript &opret) { - if (V::EvalCode() == EVAL_TOKENS) { - 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()) { @@ -301,7 +274,7 @@ UniValue TokenFinalizeTransferTx(CMutableTransaction &mtx, struct CCcontract_inf // 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 = V::FinalizeCCTx(isRemote, 0LL, cp, mtx, mypk, txfee, opret); + UniValue sigData = V::FinalizeCCTx(isRemote, FINALIZECCTX_NO_CHANGE_WHEN_DUST, 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 @@ -320,21 +293,21 @@ UniValue TokenFinalizeTransferTx(CMutableTransaction &mtx, struct CCcontract_inf // 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 +// destinations - addresses or pubkeys where to send tokens // total - token amount to transfer // returns: signed transfer tx in hex template -UniValue TokenTransferExt(const CPubKey &remotepk, CAmount txfee, uint256 tokenid, const char *tokenaddr, std::vector> probeconds, uint8_t M, std::vector destpubkeys, CAmount total, bool useMempool) +UniValue TokenTransferExtDest(const CPubKey &remotepk, CAmount txfee, uint256 tokenid, const std::vector &tokenaddrs, std::vector> probeconds, uint8_t M, std::vector destinations, CAmount total, bool useMempool, bool spendMarker) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CAmount CCchange = 0, inputs = 0; - struct CCcontract_info *cp, C; if (total < 0) { CCerror = strprintf("negative total"); LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << CCerror << "=" << total << std::endl); return NullUniValue; } + + struct CCcontract_info *cp, C; cp = CCinit(&C, V::EvalCode()); if (txfee == 0) @@ -346,48 +319,63 @@ UniValue TokenTransferExt(const CPubKey &remotepk, CAmount txfee, uint256 tokeni CCerror = "mypk is not set or invalid"; return NullUniValue; } + std::vector destpubkeys; + for (auto const &dest : destinations) + if (dest.which() == TX_PUBKEY) + destpubkeys.push_back(boost::get(dest)); // CAmount normalInputs = AddNormalinputs(mtx, mypk, txfee, 0x10000, isRemote); // note: wallet scanning for inputs is slower than index scanning CAmount normalInputs = AddNormalinputsRemote(mtx, mypk, txfee, 0x10000, useMempool); if (normalInputs > 0) { - if ((inputs = AddTokenCCInputs(cp, mtx, tokenaddr, tokenid, total, CC_MAXVINS, useMempool)) >= total) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx! - { - uint8_t destEvalCode = V::EvalCode(); - if (cp->evalcodeNFT != 0) // if set in AddTokenCCInputs - { - destEvalCode = cp->evalcodeNFT; - } - - if (inputs > total) - CCchange = (inputs - total); + CAmount CCchange = 0, CCinputs = 0; + for (const auto &addr : tokenaddrs) { + CAmount CCinputsOne = AddTokenCCInputs(cp, mtx, addr.c_str(), tokenid, total, CC_MAXVINS, useMempool); + if (CCinputsOne > 0) + CCinputs += CCinputsOne; + if (CCinputs >= total) + break; + } - if (destpubkeys.size() == 0) { + if (spendMarker) { + mtx.vin.push_back(CTxIn(tokenid, 0)); // spend tokenlist marker + } + + if (CCinputs >= total) + { + if (CCinputs > total) + CCchange = (CCinputs - total); + + if (destinations.size() == 0) { CCerror = "no dest pubkeys"; return NullUniValue; } if (V::EvalCode() == EVAL_TOKENS) { - if (destpubkeys.size() > 2) { + if (destinations.size() > 2) { CCerror = "no more than 2 dest pubkeys supported"; return NullUniValue; } } if (V::EvalCode() == EVAL_TOKENSV2) { - if (destpubkeys.size() > 128) { + if (destinations.size() > 128) { CCerror = "no more than 128 dest pubkeys supported"; return NullUniValue; } } - mtx.vout.push_back(V::MakeTokensCCMofNvout(destEvalCode, 0, total, M, destpubkeys)); + mtx.vout.push_back(V::MakeTokensCCMofNDestVout(V::EvalCode(), 0, total, M, destinations)); // add optional custom probe conds to non-usual sign vins for (const auto &p : probeconds) CCAddVintxCond(cp, p.first, p.second); - + if (V::EvalCode() == EVAL_TOKENSV2) { + // probes for spending from mypk + for (const auto &mycond : GetTokenV2Conds(mypk)) + CCAddVintxCond(cp, mycond, nullptr); + // if this is multisig - build and add multisig probes: // find any token vin, load vin tx and extract M and pubkeys for(int ccvin = 0; ccvin < mtx.vin.size(); ccvin ++) { @@ -403,10 +391,10 @@ UniValue TokenTransferExt(const CPubKey &remotepk, CAmount txfee, uint256 tokeni COptCCParams ccparams(vParams[0]); if (ccparams.version != 0 && ccparams.vKeys.size() > 1) { if (CCchange != 0) { - mtx.vout.push_back(V::MakeTokensCCMofNvout(destEvalCode, 0, CCchange, ccparams.m, ccparams.vKeys)); + mtx.vout.push_back(V::MakeTokensCCMofNvout(V::EvalCode(), 0, CCchange, ccparams.m, ccparams.vKeys)); CCchange = 0; } - CCwrapper ccprobeMofN( MakeTokensv2CCcondMofN(destEvalCode, 0, ccparams.m, ccparams.vKeys) ); + CCwrapper ccprobeMofN( MakeTokensv2CCcondMofN(V::EvalCode(), 0, ccparams.m, ccparams.vKeys) ); CCAddVintxCond(cp, ccprobeMofN, nullptr); //add MofN probe to find vins and sign break; } @@ -414,23 +402,25 @@ UniValue TokenTransferExt(const CPubKey &remotepk, CAmount txfee, uint256 tokeni } } - if (CCchange != 0) - mtx.vout.push_back(V::MakeTokensCC1vout(destEvalCode, CCchange, mypk)); - - // no need this: this prbe added in FinalizeCCV2Tx - //CCwrapper ccprobeNFT( MakeTokensv2CCcond1(destEvalCode, mypk) ); - //CCAddVintxCond(cp, ccprobeNFT, nullptr); //add NFT probe to find vins and sign + if (CCchange != 0) { + if (V::EvalCode() == EVAL_TOKENSV2 && + CCUpgrades::IsUpgradeActive(chainActive.LastTip()->GetMedianTimePast(), chainActive.LastTip()->GetHeight() + 1, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)) { + mtx.vout.push_back(V::MakeTokensCCMofNDestVout(V::EvalCode(), 0, CCchange, 1, {mypk.GetID()})); // send change to R-address after the HF + } + else + mtx.vout.push_back(V::MakeTokensCC1vout(V::EvalCode(), CCchange, mypk)); + } // 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 = V::FinalizeCCTx(isRemote, 0LL, cp, mtx, mypk, txfee, V::EncodeTokenOpRet(tokenid, destpubkeys, {} )); + UniValue sigData = V::FinalizeCCTx(isRemote, FINALIZECCTX_NO_CHANGE_WHEN_DUST, cp, mtx, mypk, txfee, V::EncodeTokenOpRet(tokenid, destpubkeys, {} )); if (!ResultHasTx(sigData)) CCerror = "could not finalize tx"; return sigData; } else { - if (inputs == 0LL) + if (CCinputs == 0LL) CCerror = strprintf("no token inputs"); else CCerror = strprintf("insufficient token inputs"); @@ -444,28 +434,48 @@ UniValue TokenTransferExt(const CPubKey &remotepk, CAmount txfee, uint256 tokeni return NullUniValue; } -// transfer tokens from mypk to another pubkey -// param additionalEvalCode2 allows transfer of dual-eval non-fungible tokens +// old style call, convert pubkeys to destinations +template +UniValue TokenTransferExt(const CPubKey &remotepk, CAmount txfee, uint256 tokenid, const std::vector &tokenaddrs, std::vector> probeconds, uint8_t M, const std::vector &destpks, CAmount total, bool useMempool) +{ + std::vector destinations; + const bool spendMarker = false; + for (auto const &pk : destpks) + destinations.push_back(pk); + return TokenTransferExtDest(remotepk, txfee, tokenid, tokenaddrs, probeconds, M, destinations, total, useMempool, spendMarker); +} + +// transfer tokens from mypk to a destination template -std::string TokenTransfer(CAmount txfee, uint256 tokenid, uint8_t M, const std::vector &destpubkeys, CAmount total) +std::string TokenTransferDest(CAmount txfee, uint256 tokenid, uint8_t M, const std::vector &destinations, CAmount total, bool spendMarker) { - char tokenaddr[KOMODO_ADDRESS_BUFSIZE]; CPubKey mypk = pubkey2pk(Mypubkey()); struct CCcontract_info *cp, C; cp = CCinit(&C, V::EvalCode()); TokenDataTuple tokenData; - vscript_t vopretNonfungible; - GetTokenData(tokenid, tokenData, vopretNonfungible); - if (vopretNonfungible.size() > 0) - cp->evalcodeNFT = vopretNonfungible.begin()[0]; // set evalcode of NFT - GetTokensCCaddress(cp, tokenaddr, mypk, V::IsMixed()); + vuint8_t vextraData; + GetTokenData(NULL, tokenid, tokenData, vextraData); + //vuint8_t vextraData = std::get<4>(tokenData); - UniValue sigData = TokenTransferExt(CPubKey(), txfee, tokenid, tokenaddr, {}, M, destpubkeys, total, true); + std::vector tokenindexkeys = V::GetTokenIndexKeys(mypk); + UniValue sigData = TokenTransferExtDest(CPubKey(), txfee, tokenid, tokenindexkeys, {}, M, destinations, total, false, spendMarker); return ResultGetTx(sigData); } +// old style call, convert pubkeys to destinations +template +std::string TokenTransfer(CAmount txfee, uint256 tokenid, uint8_t M, const std::vector &destpks, CAmount total) +{ + std::vector destinations; + const bool spendMarker = false; + for (auto const &pk : destpks) + destinations.push_back(pk); + return TokenTransferDest(txfee, tokenid, M, destinations, total, spendMarker); +} + + // returns token creation signed raw tx // params: txfee amount, token amount, token name and description, optional NFT data, optional eval code of a cc to validate NFT template @@ -480,25 +490,25 @@ UniValue CreateTokenExt(const CPubKey &remotepk, CAmount txfee, CAmount tokensup LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << CCerror << "=" << tokensupply << std::endl); return NullUniValue; } - if (!nonfungibleData.empty() && tokensupply != 1) { + /*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; - } + }*/ cp = CCinit(&C, V::EvalCode()); - if (name.size() > 32 || description.size() > 4096) // this is also checked on rpc level + if (name.size() > TOKENS_MAX_NAME_LENGTH || description.size() > TOKENS_MAX_DESC_LENGTH) // 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); - CCerror = "name should be <= 32, description should be <= " + std::to_string(4096); + CCerror = "name or description too long"; return NullUniValue; } if (txfee == 0) txfee = 10000; - int32_t txfeeCount = 2; + int32_t markerCount = 1; if (additionalMarkerEvalCode > 0) - txfeeCount++; + markerCount++; bool isRemote = remotepk.IsValid(); CPubKey mypk = isRemote ? remotepk : pubkey2pk(Mypubkey()); @@ -510,31 +520,31 @@ UniValue CreateTokenExt(const CPubKey &remotepk, CAmount txfee, CAmount tokensup 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, useMempool)) > 0) + if ((totalInputs = AddNormalinputsRemote(mtx, mypk, tokensupply + txfee + markerCount * TOKENS_MARKER_VALUE, 0x10000, useMempool)) > 0) { - CAmount mypkInputs = TotalPubkeyNormalInputs(mtx, mypk); + CAmount mypkInputs = TotalPubkeyNormalInputs(nullptr, 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; } - uint8_t destEvalCode = V::EvalCode(); - if( nonfungibleData.size() > 0 && nonfungibleData[0] != 0 ) - destEvalCode = nonfungibleData[0]; - // NOTE: we should prevent spending fake-tokens from this marker in IsTokenvout(): - mtx.vout.push_back(V::MakeCC1vout(V::EvalCode(), 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(V::MakeTokensCC1vout(destEvalCode, tokensupply, mypk)); + mtx.vout.push_back(V::MakeCC1vout(V::EvalCode(), TOKENS_MARKER_VALUE, GetUnspendable(cp, NULL))); // new marker to token cc addr, burnable and validated, vout pos now changed to 0 (from 1) + mtx.vout.push_back(V::MakeTokensCC1vout(V::EvalCode(), tokensupply, mypk)); if (additionalMarkerEvalCode > 0) { // add additional marker for NFT cc evalcode: struct CCcontract_info *cpNFT, CNFT; cpNFT = CCinit(&CNFT, additionalMarkerEvalCode); - mtx.vout.push_back(V::MakeCC1vout(additionalMarkerEvalCode, txfee, GetUnspendable(cpNFT, NULL))); + mtx.vout.push_back(V::MakeCC1vout(additionalMarkerEvalCode, TOKENS_MARKER_VALUE, GetUnspendable(cpNFT, NULL))); } - sigData = V::FinalizeCCTx(isRemote, FINALIZECCTX_NO_CHANGE_WHEN_ZERO, cp, mtx, mypk, txfee, V::EncodeTokenCreateOpRet(vscript_t(mypk.begin(), mypk.end()), name, description, { nonfungibleData })); + std::vector vdatas; + if (!nonfungibleData.empty()) + vdatas.push_back(nonfungibleData); + // prevent adding dust change in tokens + sigData = V::FinalizeCCTx(isRemote, FINALIZECCTX_NO_CHANGE_WHEN_DUST, cp, mtx, mypk, txfee, V::EncodeTokenCreateOpRet(vscript_t(mypk.begin(), mypk.end()), name, description, vdatas)); if (!ResultHasTx(sigData)) { CCerror = "couldnt finalize token tx"; return NullUniValue; @@ -587,15 +597,101 @@ CAmount GetTokenBalance(CPubKey pk, uint256 tokenid, bool usemempool) return(AddTokenCCInputs(cp, mtx, pk, tokenid, 0, 0, usemempool)); } -template -UniValue TokenInfo(uint256 tokenid) +template +UniValue GetAllTokenBalances(CPubKey pk, bool useMempool) +{ + const bool CC_INPUTS_TRUE = true; + const char *funcname = __func__; + + UniValue result(UniValue::VARR); + + struct CCcontract_info *cp, C; + cp = CCinit(&C, V::EvalCode()); + + std::vector tokenindexkeys = V::GetTokenIndexKeys(pk); + + std::map mapBalances; + + // make lambda to use it for either index kind: + auto add_token_amount = [&](const char *tokenindexkey, uint256 txhash, int32_t index, CAmount satoshis) -> void + { + CTransaction tx; + uint256 hashBlock; + + if (satoshis == 0) + return; // skip null utxos + + if (myGetTransaction(txhash, tx, hashBlock) != 0) + { + char destaddr[KOMODO_ADDRESS_BUFSIZE]; + Getscriptaddress(destaddr, tx.vout[index].scriptPubKey); + if (strcmp(destaddr, tokenindexkey) != 0) + return; + + LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << funcname << "()" << " checking tx vout destaddress=" << destaddr << " amount=" << tx.vout[index].nValue << std::endl); + + uint8_t funcId = 0; + uint256 tokenIdOut; + CScript opret; + std::string errorStr; + + CAmount retAmount = V::CheckTokensvout(cp, NULL, tx, index, opret, tokenIdOut, funcId, errorStr); + + if (retAmount > 0 && !myIsutxo_spentinmempool(ignoretxid, ignorevin, txhash, index)) + { + CAmount prevAmount = mapBalances[tokenIdOut]; + mapBalances[tokenIdOut] = prevAmount + retAmount; + } + } + }; // auto add_token_amount + + for (std::string &tokenindexkey : tokenindexkeys) + { + if (fUnspentCCIndex) + { + std::vector > unspentOutputs; + + SetCCunspentsCCIndex(unspentOutputs, tokenindexkey.c_str()); + if (useMempool) + AddCCunspentsCCIndexMempool(unspentOutputs, tokenindexkey.c_str()); + + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " unspent ccindex found unspentOutputs=" << unspentOutputs.size() << std::endl); + for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) + add_token_amount(tokenindexkey.c_str(), it->first.txhash, it->first.index, it->second.satoshis); + } + else + { + std::vector > unspentOutputs; + + if (useMempool) + SetCCunspentsWithMempool(unspentOutputs, (char*)tokenindexkey.c_str(), CC_INPUTS_TRUE); + else + SetCCunspents(unspentOutputs, (char*)tokenindexkey.c_str(), CC_INPUTS_TRUE); + + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " unspent index found unspentOutputs=" << unspentOutputs.size() << std::endl); + for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) + add_token_amount(tokenindexkey.c_str(), it->first.txhash, it->first.index, it->second.satoshis); + } + } + + for(auto const &m : mapBalances) { + UniValue elem(UniValue::VOBJ); + elem.pushKV(m.first.GetHex(), m.second); + result.push_back(elem); + } + + return result; +} + +template +UniValue TokenInfo(uint256 tokenid, E parseExtraData) { UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction tokenbaseTx; std::vector origpubkey; std::vector oprets; - vscript_t vopretNonfungible; + vscript_t vextraData; std::string name, description; uint8_t version; @@ -628,18 +724,23 @@ UniValue TokenInfo(uint256 tokenid) result.push_back(Pair("owner", HexStr(origpubkey))); result.push_back(Pair("name", name)); - CAmount supply = 0, output; for (int v = 0; v < tokenbaseTx.vout.size(); v++) - if ((output = IsTokensvout(false, true, cpTokens, NULL, tokenbaseTx, v, tokenid)) > 0) + if ((output = IsTokensvout(cpTokens, NULL, tokenbaseTx, v, tokenid)) > 0) supply += output; result.push_back(Pair("supply", supply)); result.push_back(Pair("description", description)); if (oprets.size() > 0) - vopretNonfungible = oprets[0]; - if( !vopretNonfungible.empty() ) - result.push_back(Pair("data", HexStr(vopretNonfungible))); + vextraData = oprets[0]; + if( !vextraData.empty() ) { + result.push_back(Pair("data", HexStr(vextraData))); + UniValue extraDataAsJson = parseExtraData(vextraData); + if (!extraDataAsJson.isNull()) + result.push_back(Pair("dataAsJson", extraDataAsJson)); + + } + result.push_back(Pair("version", DecodeTokenOpretVersion(tokenbaseTx.vout.back().scriptPubKey))); result.push_back(Pair("IsMixed", V::EvalCode() == TokensV2::EvalCode() ? "yes" : "no")); @@ -677,6 +778,18 @@ UniValue TokenInfo(uint256 tokenid) result.push_back(Pair("sourceTokenId", sourceTokenId)); } + LOCK(cs_main); + int32_t nHeight = -1; + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) { + CBlockIndex* pindex = (*mi).second; + if (chainActive.Contains(pindex)) { + nHeight = pindex->GetHeight(); + } else { + nHeight = -1; + } + } + result.push_back(Pair("height", nHeight)); return result; } @@ -789,18 +902,20 @@ static uint8_t ValidateTokenOpret(uint256 txid, const CScript &scriptPubKey, uin // checks if any token vouts are sent to 'dead' pubkey template -static CAmount HasBurnedTokensvouts(const CTransaction& tx, uint256 reftokenid) +static CAmount HasBurnedTokensvouts(Eval *eval, const CTransaction& tx, uint256 reftokenid) { uint8_t dummyEvalCode; uint256 tokenIdOpret; - std::vector vDeadPubkeys, voutPubkeysDummy; + std::vector voutPubkeysDummy; std::vector oprets; TokenDataTuple tokenData; - vscript_t vopretExtra, vopretNonfungible; + vscript_t vopretExtra, vextraData; uint8_t evalCode = V::EvalCode(); // 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 + const bool isSubver1 = CCUpgrades::IsUpgradeActive(eval->GetCurrentTime(), eval->GetCurrentHeight(), CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1); + // test vouts for possible token use-cases: std::vector> testVouts; @@ -824,9 +939,9 @@ static CAmount HasBurnedTokensvouts(const CTransaction& tx, uint256 reftokenid) LOGSTREAMFN(cctokens_log, CCLOG_DEBUG2, stream << "vopretExtra=" << HexStr(vopretExtra) << std::endl); - GetTokenData(reftokenid, tokenData, vopretNonfungible); - if (vopretNonfungible.size() > 0) - evalCode = vopretNonfungible.begin()[0]; + GetTokenData(eval, reftokenid, tokenData, vextraData); + if (vextraData.size() > 0) + evalCode = vextraData.begin()[0]; if (vopretExtra.size() > 0) evalCode2 = vopretExtra.begin()[0]; @@ -835,7 +950,7 @@ static CAmount HasBurnedTokensvouts(const CTransaction& tx, uint256 reftokenid) evalCode2 = 0; } - vDeadPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY))); + std::vector vDeadPubkeys = GetBurnPubKeys(eval->GetCurrentTime(), eval->GetCurrentHeight()); CAmount burnedAmount = 0; @@ -858,7 +973,9 @@ static CAmount HasBurnedTokensvouts(const CTransaction& tx, uint256 reftokenid) // try all test vouts: for (const auto &t : testVouts) { - if (t.first == tx.vout[i]) { + //if (t.first == tx.vout[i]) { + if (!isSubver1 && t.first == tx.vout[i] || + isSubver1 && IsEqualDestinations(t.first.scriptPubKey, tx.vout[i].scriptPubKey)) { 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! @@ -871,16 +988,20 @@ static CAmount HasBurnedTokensvouts(const CTransaction& tx, uint256 reftokenid) return burnedAmount; } +// checks if this is tokens marker (sent to the token shared address) +// and the marker value is fixed +// returns -1 if this is a bad marker or marker's nValue +// return 0 if this is not a token marker template -bool IsTokenMarkerVout(CTxOut vout) { +CAmount IsTokenMarkerVout(CTxOut vout) { struct CCcontract_info *cpTokens, CCtokens_info; cpTokens = CCinit(&CCtokens_info, V::EvalCode()); - return IsEqualScriptPubKeys(vout.scriptPubKey, V::MakeCC1vout(V::EvalCode(), vout.nValue, GetUnspendable(cpTokens, NULL)).scriptPubKey); + if (IsEqualDestinations(vout.scriptPubKey, V::MakeCC1vout(V::EvalCode(), vout.nValue, GetUnspendable(cpTokens, NULL)).scriptPubKey)) + return vout.nValue == TOKENS_MARKER_VALUE ? vout.nValue : -1; + else + return 0; } - - - // 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 @@ -889,11 +1010,15 @@ bool IsTokenMarkerVout(CTxOut vout) { // 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! template -CAmount IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true*/, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid) +CAmount IsTokensvout(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid) { + uint8_t funcId = 0; uint256 tokenIdInOpret; + CScript opret; std::string errorStr; - CAmount retAmount = V::CheckTokensvout(goDeeper, checkPubkeys, cp, eval, tx, v, tokenIdInOpret, errorStr); + + CAmount retAmount = V::CheckTokensvout(cp, eval, tx, v, opret, tokenIdInOpret, funcId, errorStr); + // std::cerr << __func__ << " tokenIdInOpret=" << tokenIdInOpret.GetHex() << " funcId=" << (int)funcId << std::endl; if (!errorStr.empty()) LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "error=" << errorStr << std::endl); if (retAmount < 0) @@ -920,22 +1045,22 @@ static bool CheckMarkerSpending(struct CCcontract_info *cp, Eval *eval, const CT if (vin.prevout.hash == tokenid) // check if this is my marker { // calc burned amount - CAmount burnedAmount = HasBurnedTokensvouts(tx, tokenid); + CAmount burnedAmount = HasBurnedTokensvouts(eval, tx, tokenid); if (burnedAmount > 0) { TokenDataTuple tokenData; - vscript_t vopretNonfungible; - GetTokenData(tokenid, tokenData, vopretNonfungible); - if (!vopretNonfungible.empty()) + vscript_t vextraData; + GetTokenData(eval, tokenid, tokenData, vextraData); + if (!vextraData.empty()) { CTransaction tokenbaseTx; uint256 hashBlock; - if (myGetTransaction(tokenid, tokenbaseTx, hashBlock)) + if (GetTxUnconfirmedOpt(eval, 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) + if ((output = IsTokensvout(cp, eval, tokenbaseTx, v, tokenid)) > 0) supply += output; if (supply == 1 && supply == burnedAmount) // burning marker is allowed only for burned NFTs (that is with supply == 1) @@ -958,54 +1083,49 @@ bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, Eval* eval, c CTransaction vinTx; uint256 hashBlock; CAmount tokenoshis; - - //struct CCcontract_info *cpTokens, tokensC; - //cpTokens = CCinit(&tokensC, EVAL_TOKENS); - - int32_t numvins = tx.vin.size(); - int32_t numvouts = tx.vout.size(); + const char *funcname = __func__; std::map mapinputs, mapoutputs; // 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); - - // 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*/) + // pick token vouts in vin transactions and calculate total inputs + for (int32_t i = 0; i < tx.vin.size(); i ++) + { + if ((*cp->ismyvin)(tx.vin[i].scriptSig)) { - //std::cerr << indentStr << __func__ << " 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 (!GetTxUnconfirmedOpt(eval, 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); + LOGSTREAM(cctokens_log, CCLOG_ERROR, stream << indentStr << funcname << "()" << " cannot read vintx for vin i=" << i << std::endl); return (!eval) ? false : eval->Invalid("could not load vin tx " + std::to_string(i)); } else { - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "TokensExactAmounts() checking vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl); + LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << funcname << "()" << " checking vintx.vout for cc tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl); uint256 reftokenid; + uint8_t funcId = 0; + CScript opret; // validate vouts of vintx tokenValIndentSize++; - tokenoshis = V::CheckTokensvout(goDeeper, true, cp, eval, vinTx, tx.vin[i].prevout.n, reftokenid, errorStr); + tokenoshis = V::CheckTokensvout(cp, eval, vinTx, tx.vin[i].prevout.n, opret, reftokenid, funcId, errorStr); + // std::cerr << __func__ << " reftokenid=" << reftokenid.GetHex() << " vin=" << i << " funcId=" << (int)funcId << " " << funcId << std::endl; 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 << "TokensExactAmounts() skipping marker vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl); + if (IsTokenMarkerVout(vinTx.vout[tx.vin[i].prevout.n]) > 0LL) { + LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << funcname << "()" << " skipping marker vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl); continue; + // do not check for marker count for on-chain transacitions, no point in checking this (for token v1) as it is an antispam feature } if (tokenoshis != 0) { - LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding vintx.vout for tx.vin[" << i << "] tokenoshis=" << tokenoshis << std::endl); + LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << funcname << "()" << " adding vintx.vout for tx.vin[" << i << "] tokenoshis=" << tokenoshis << std::endl); mapinputs[reftokenid] += tokenoshis; } } @@ -1013,53 +1133,134 @@ bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, Eval* eval, c } // pick token vouts in the current transaction and calculate output total - for (int32_t i = 0; i < numvouts; i ++) + int markerVouts = 0; + int createVouts = 0; + int transferVouts = 0; + CScript opret; + for (int32_t i = 0; i < tx.vout.size(); i ++) { - uint256 reftokenid; - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "TokensExactAmounts() recursively checking tx.vout[" << i << "] nValue=" << tx.vout[i].nValue << std::endl); - - // Note: we pass in here IsTokensvout(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 = V::CheckTokensvout(false, true, cp, eval, tx, i, reftokenid, errorStr); - tokenValIndentSize--; - if (tokenoshis < 0) - return false; + if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition()) + { + uint256 reftokenid; + uint8_t funcId = 0; + LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << funcname << "()" << " checking cc tx.vout[" << i << "] nValue=" << tx.vout[i].nValue << std::endl); + + // indeed, if we pass 'true' we'll be checking this tx vout again + tokenValIndentSize++; + tokenoshis = V::CheckTokensvout(cp, eval, tx, i, opret, reftokenid, funcId, errorStr); + // std::cerr << __func__ << " reftokenid=" << reftokenid.GetHex() << " vout=" << i << " funcId=" << (int)funcId << " " << funcId << std::endl; + tokenValIndentSize--; + if (tokenoshis < 0) + return false; - if (IsTokenMarkerVout(tx.vout[i])) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << "TokensExactAmounts() skipping marker tx.vout[" << i << "] nValue=" << tx.vout[i].nValue << std::endl); - continue; - } + CAmount markerAmount = IsTokenMarkerVout(tx.vout[i]); + if (markerAmount > 0) { + ++ markerVouts; + if (IsTokenCreateFuncid(funcId) && markerVouts > 1) { + errorStr = "tokencreate cannot have more than one marker"; + return false; + } - if (tokenoshis != 0) - { - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding tx.vout[" << i << "] tokenoshis=" << tokenoshis << std::endl); - mapoutputs[reftokenid] += tokenoshis; - } + LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << indentStr << funcname << "()" << " skipping marker tx.vout[" << i << "] nValue=" << tx.vout[i].nValue << std::endl); + continue; // skip marker + } + else if (markerAmount < 0) { + errorStr = "invalid marker value"; + return false; + } + + if (IsTokenCreateFuncid(funcId)) + ++ createVouts; + if (IsTokenTransferFuncid(funcId)) + ++ transferVouts; + + if (tokenoshis != 0) + { + LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << funcname << "()" << " adding tx.vout[" << i << "] tokenoshis=" << tokenoshis << std::endl); + mapoutputs[reftokenid] += tokenoshis; + } + } } - //std::cerr << indentStr << "TokensExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; + // can't mix tokencreate and tokentransfer + if (createVouts > 0 && transferVouts > 0) { + errorStr = "can't have both create and transfer vouts"; + return false; + } + + // tokencreate checks (this would work only for tokens v2 as tokens v1 tokencreate does not pass cc validation when it is added to the chain) + if (createVouts > 0) + { + // check that creation tx does not have my cc vins + bool hasMyccvin = false; + std::for_each (tx.vin.begin(), tx.vin.end(), [&](const CTxIn &vin){ cp->ismyvin(vin.scriptSig) ? hasMyccvin = true : hasMyccvin = hasMyccvin; }); + if (hasMyccvin) { + errorStr = "creation tx can't have token vins"; + return false; + } + if (createVouts > 1) { + errorStr = "creation tx can't have several token vouts"; + return false; + } + // marker antispam check: + if (markerVouts > 1) { + errorStr = "tokencreate cannot have more than one marker"; + return false; + } + + std::vector vpksdummy; + std::vector oprets; + vuint8_t vorigpk; + std::string name, description; + + TokensV2::DecodeTokenCreateOpRet(opret, vorigpk, name, description, oprets); + + // check this is really creator + CPubKey origpk = pubkey2pk(vorigpk); + if (TotalPubkeyNormalInputs(eval, tx, origpk) == 0) { + errorStr = "no vins signed with creator pubkey"; + return false; + } - if (mapinputs.size() > 0 && mapinputs.size() == mapoutputs.size()) + // can't create tokens with global key + if (origpk == GetUnspendable(cp, NULL)) { + errorStr = "cannot create tokens with token shared pubkey"; + return false; + } + return true; // tokencreate checks finished + } + + // tokentransfer checks + if (transferVouts > 0) { - 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 = "cc inputs not equal outputs for tokenid=" + m.first.GetHex(); - return false; - } + // markers are not allowed for tokentransfer (for antispam reasons) + if (markerVouts > 0) { + errorStr = "tokentransfer cannot have markers"; + return false; + } - // check marker spending: - if (!CheckMarkerSpending(cp, eval, tx, m.first)) { - errorStr = "marker spending is not allowed for tokenid=" + m.first.GetHex(); - return false; + // check token value balance: + if (mapinputs.size() > 0 && mapinputs.size() == mapoutputs.size()) + { + for(auto const &m : mapinputs) { + LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << funcname << "()" << " inputs[" << m.first.GetHex() << "]=" << m.second << " outputs=" << mapoutputs[m.first] << std::endl); + if (m.second != mapoutputs[m.first]) { + errorStr = "cc inputs not equal outputs for tokenid=" + m.first.GetHex(); + return false; + } + + // check marker spending: + if (!CheckMarkerSpending(cp, eval, tx, m.first)) { + errorStr = "marker spending is not allowed for tokenid=" + m.first.GetHex(); + return false; + } + LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << funcname << "()" << " mapinput.second=" << m.second << " mapoutputs[m.first]=" << mapoutputs[m.first] << std::endl); } - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() mapinput.second=" << m.second << " mapoutputs[m.first]=" << mapoutputs[m.first] << std::endl); + return true; } - return true; - } - LOGSTREAM(cctokens_log, CCLOG_INFO, stream << indentStr << "TokensExactAmounts() no cc inputs or cc outputs for a tokenid, mapinputs.size()=" << mapinputs.size() << " mapoutputs.size()=" << mapoutputs.size() << std::endl); - errorStr = "no cc vins or cc vouts for tokenid"; + } + LOGSTREAM(cctokens_log, CCLOG_INFO, stream << indentStr << funcname << "()" << " no cc inputs or cc outputs, mapinputs.size()=" << mapinputs.size() << " mapoutputs.size()=" << mapoutputs.size() << std::endl); + errorStr = "no tokens cc vins or cc vouts"; return false; } diff --git a/src/cc/CCtokenutils.cpp b/src/cc/CCtokenutils.cpp index ee124966bb1..f7e205b53ef 100644 --- a/src/cc/CCtokenutils.cpp +++ b/src/cc/CCtokenutils.cpp @@ -17,103 +17,15 @@ // 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 "key_io.h" #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 - - -// 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) -{ - return true; // aleays true for tokel chains -} - -// 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; -} - -// 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; - - 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)); - } - } - return opretswithid; -} CScript EncodeTokenCreateOpRetV1(const std::vector &origpubkey, const std::string &name, const std::string &description, const std::vector &oprets) { - /* no tokens v0 for tokel - // call compatibility code: - if (!TokensIsVer1Active(NULL)) { - return tokensv0::EncodeTokenCreateOpRet('c', origpubkey, name, description, CreationOpretsToOpretsWithId(oprets)); // route to the previous version - } - */ - CScript opret; uint8_t evalcode = EVAL_TOKENS; - uint8_t funcid = 'C'; // 'C' indicates v1 - uint8_t version = 1; + uint8_t funcid = 'C'; // 'C' indicates opreturn version 1 (with the version field) + uint8_t version = TOKENS_OPRETURN_VERSION; opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << version << origpubkey << name << description; for (const auto &o : oprets) { @@ -127,7 +39,7 @@ CScript EncodeTokenCreateOpRetV2(const std::vector &origpubkey, const s CScript opret; uint8_t evalcode = EVAL_TOKENSV2; uint8_t funcid = 'c'; - uint8_t version = 1; + uint8_t version = TOKENS_OPRETURN_VERSION; opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << version << origpubkey << name << description; for (const auto &o : oprets) { @@ -139,17 +51,10 @@ CScript EncodeTokenCreateOpRetV2(const std::vector &origpubkey, const s // v1 format with no opretid (evalcode is used instead) CScript EncodeTokenOpRetV1(uint256 tokenid, const std::vector &voutPubkeys, const std::vector &oprets) { - /* no tokens v0 for tokel - // 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'; // 'T' indicates opreturn version 1 (with the version field) uint8_t evalCodeInOpret = EVAL_TOKENS; - uint8_t version = 1; + uint8_t version = TOKENS_OPRETURN_VERSION; tokenid = revuint256(tokenid); @@ -160,9 +65,6 @@ CScript EncodeTokenOpRetV1(uint256 tokenid, const std::vector &voutPubk LOGSTREAM(cctokens_log, 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]; @@ -179,7 +81,7 @@ CScript EncodeTokenOpRetV2(uint256 tokenid, const std::vector &oprets CScript opret; uint8_t tokenFuncId = 't'; uint8_t evalCodeInOpret = EVAL_TOKENSV2; - uint8_t version = 1; + uint8_t version = TOKENS_OPRETURN_VERSION; tokenid = revuint256(tokenid); @@ -206,19 +108,6 @@ uint8_t DecodeTokenCreateOpRetV1(const CScript &scriptPubKey, 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') { @@ -273,18 +162,6 @@ uint8_t DecodeTokenOpRetV1(const CScript scriptPubKey, uint256 &tokenid, std::ve oprets.clear(); - /* no tokens v0 for tokel - // 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; - } - */ - GetOpReturnData(scriptPubKey, vopret); // tokenid = zeroid; this was incorrect: cleared the passed tokenid if creation tx @@ -364,7 +241,7 @@ uint8_t DecodeTokenOpRetV2(const CScript scriptPubKey, uint256 &tokenid, std::ve LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "incorrect evalcode in tokens v2 opret" << std::endl); return (uint8_t)0; } - if (version != 1) { + if (version != TOKENS_OPRETURN_VERSION) { LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "incorrect version in tokens v2 opret" << std::endl); return (uint8_t)0; } @@ -493,13 +370,21 @@ CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk, std::vect // token v2 'mixed' vouts: -// make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition: -CC *MakeTokensv2CCcondMofN(uint8_t evalcode1, uint8_t evalcode2, uint8_t M, std::vector pks) +// make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition with pubkeys or keyids: +CC *MakeTokensv2CCcondMofNDest(uint8_t evalcode1, uint8_t evalcode2, uint8_t M, std::vector dests) { - // make 1of2 sigs cond - std::vector condpks; - for (auto const &pk : pks) - condpks.push_back(CCNewSecp256k1(pk)); + // make MofN sigs cond with destinations + std::vector conddests; + for (auto const &dest : dests) { + CC *ccSig; + if (dest.which() == TX_PUBKEY) + ccSig = CCNewSecp256k1(boost::get(dest)); + else if (dest.which() == TX_PUBKEYHASH) + ccSig = CCNewSecp256k1Hash(boost::get(dest)); + else + return nullptr; + conddests.push_back(ccSig); + } std::vector thresholds; if (evalcode1 != 0) @@ -508,10 +393,23 @@ CC *MakeTokensv2CCcondMofN(uint8_t evalcode1, uint8_t evalcode2, uint8_t M, std: thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENSV2))); // this is eval token cc if (evalcode2 != 0) thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode - thresholds.push_back(CCNewThreshold(M, condpks)); // this is 1 of 2 sigs cc + thresholds.push_back(CCNewThreshold(M, conddests)); // this is 1 of 2 sigs cc return CCNewThreshold(thresholds.size(), thresholds); } + +// make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition with pubkeys: +CC *MakeTokensv2CCcondMofN(uint8_t evalcode1, uint8_t evalcode2, uint8_t M, std::vector pks) +{ + // convert pks to dests + std::vector dests; + for (auto const &pk : pks) + dests.push_back(pk); + // make MofN sigs cond + return MakeTokensv2CCcondMofNDest(evalcode1, evalcode2, M, dests); +} + + // overload to make two-eval (token+evalcode) 1of2 cryptocondition: CC *MakeTokensv2CCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) { return MakeTokensv2CCcondMofN(evalcode, 0, 1, { pk1, pk2 }); @@ -533,20 +431,28 @@ CC *MakeTokensv2CCcond1(uint8_t evalcode, CPubKey pk) { return MakeTokensv2CCcondMofN(evalcode, 0, 1, { pk }); } -// make three-eval (token+evalcode+evalcode2) MofN cc vout: -CTxOut MakeTokensCCMofNvoutMixed(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, uint8_t M, const std::vector &pks, vscript_t* pvData) +CTxOut MakeTokensCCMofNDestVoutMixed(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, uint8_t M, const std::vector &dests, vscript_t* pvData) { CTxOut vout; - CCwrapper payoutCond( MakeTokensv2CCcondMofN(evalcode1, evalcode2, M, pks) ); - if (!CCtoAnon(payoutCond.get())) - return vout; - vout = CTxOut(nValue, CCPubKey(payoutCond.get(),true)); + CCwrapper payoutCond( MakeTokensv2CCcondMofNDest(evalcode1, evalcode2, M, dests) ); + //if (!CCtoAnon(payoutCond.get())) + // return vout; + + bool hasSecHash = std::find_if(dests.begin(), dests.end(), [](const CTxDestination &dest){ return dest.which() == TX_PUBKEYHASH; }) != dests.end(); + + vout = CTxOut(nValue, CCPubKey(payoutCond.get(), hasSecHash ? CC_MIXED_MODE_SECHASH_SUBVER_1 : CC_MIXED_MODE_SUBVER_0) ); { std::vector vvData; if (pvData) vvData.push_back(*pvData); + // convert to pubkeys to show them in opdrop + std::vector pks; + for (auto const &dest : dests) + if (dest.which() == TX_PUBKEY) + pks.push_back(boost::get(dest)); + COptCCParams ccp = COptCCParams(COptCCParams::VERSION_2, evalcode1, M, pks.size(), pks, vvData); // ver2 -> add pks vout.scriptPubKey << ccp.AsVector() << OP_DROP; } @@ -555,6 +461,16 @@ CTxOut MakeTokensCCMofNvoutMixed(uint8_t evalcode1, uint8_t evalcode2, CAmount n return vout; } +// make three-eval (token+evalcode+evalcode2) MofN cc vout: +CTxOut MakeTokensCCMofNvoutMixed(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, uint8_t M, const std::vector &pks, vscript_t* pvData) +{ + // convert pks to dests + std::vector dests; + for (auto const &pk : pks) + dests.push_back(pk); + return MakeTokensCCMofNDestVoutMixed(evalcode1, evalcode2, nValue, M, dests, pvData); +} + // make three-eval (token+evalcode+evalcode2) cc vout: CTxOut MakeTokensCC1voutMixed(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, CPubKey pk, vscript_t* pvData) { @@ -613,20 +529,3 @@ uint8_t DecodeTokenOpretVersion(const CScript &scriptPubKey) } return version; } - -template -uint8_t GetTokenOpReturnVersion(uint256 tokenid) -{ - CTransaction tokencreatetx; - uint256 hashBlock; - vuint8_t vorigpk; - std::string name, desc; - std::vector oprets; - - if (myGetTransaction(tokenid, tokencreatetx, hashBlock) && - tokencreatetx.vout.size() > 0 && - V::DecodeTokenCreateOpRet(tokencreatetx.vout.back().scriptPubKey, vorigpk, name, desc, oprets) != 0) - return DecodeTokenOpretVersion(tokencreatetx.vout.back().scriptPubKey); - else - return 0; -} \ No newline at end of file diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index a420bbd773e..397cbb2c07c 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -20,13 +20,6 @@ std::vector NULL_pubkeys; struct NSPV_CCmtxinfo NSPV_U; -#ifndef FINALIZECCTX_NO_CHANGE - #define FINALIZECCTX_NO_CHANGE 0x1 -#endif -#ifndef FINALIZECCTX_NO_CHANGE_WHEN_ZERO - #define FINALIZECCTX_NO_CHANGE_WHEN_ZERO 0x2 -#endif - /* see description to function definition in CCinclude.h */ bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey) { @@ -49,15 +42,15 @@ This allows the contract transaction functions to create the appropriate vins an By using -addressindex=1, it allows tracking of all the CC addresses */ -std::string FinalizeCCTx(uint64_t CCmask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret, std::vector pubkeys) +std::string FinalizeCCTx(uint32_t changeFlag, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, CAmount txfee, CScript opret, std::vector pubkeys) { - UniValue sigData = FinalizeCCTxExt(false, CCmask, cp, mtx, mypk, txfee, opret, pubkeys); + UniValue sigData = FinalizeCCTxExt(false, changeFlag, cp, mtx, mypk, txfee, opret, pubkeys); return sigData[JSON_HEXTX].getValStr(); } // extended version that supports signInfo object with conds to vins map for remote cc calls -UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret, std::vector pubkeys) +UniValue FinalizeCCTxExt(bool remote, uint32_t changeFlag, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, CAmount txfee, CScript opret, std::vector pubkeys) { auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); CTransaction vintx; std::string hex; CPubKey globalpk; uint256 hashBlock; uint64_t mask=0,nmask=0,vinimask=0; @@ -111,7 +104,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, mypk); // to spend from single-eval EVAL_TOKENS mypk cpTokens = CCinit(&tokensC, EVAL_TOKENS); @@ -120,7 +113,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, 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. @@ -170,13 +163,22 @@ UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *c } } else fprintf(stderr,"FinalizeCCTx couldnt find %s mgret.%d\n",mtx.vin[i].prevout.hash.ToString().c_str(),mgret); } - 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 ) - { - change = totalinputs - (totaloutputs+txfee); - mtx.vout.push_back(CTxOut(change,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + //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 (changeFlag != FINALIZECCTX_NO_CHANGE) { // no need change at all (already added by the caller itself) + CAmount change = totalinputs - (totaloutputs + txfee); + CTxOut changeVout(change, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG); + if (change >= 0) + { + if ((change != 0LL || changeFlag != FINALIZECCTX_NO_CHANGE_WHEN_ZERO) && // prevent adding zero change + (!changeVout.IsDust(::minRelayTxFee) || changeFlag != FINALIZECCTX_NO_CHANGE_WHEN_DUST)) // prevent adding dust change + { + mtx.vout.push_back(changeVout); + } + } } if ( opret.size() > 0 ) mtx.vout.push_back(CTxOut(0,opret)); @@ -295,7 +297,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->tokens1of2pk[0], cp->tokens1of2pk[1]); cond = othercond1of2tokens; } else @@ -424,7 +426,7 @@ UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *c } // extended version that supports signInfo object with conds to vins map for remote cc calls - for V2 mixed mode cc vins -UniValue FinalizeCCV2Tx(bool remote, uint64_t mask, struct CCcontract_info* cp, CMutableTransaction& mtx, CPubKey mypk, uint64_t txfee, CScript opret) +UniValue FinalizeCCV2Tx(bool remote, uint32_t changeFlag, struct CCcontract_info* cp, CMutableTransaction& mtx, CPubKey mypk, CAmount txfee, CScript opret) { auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); CTransaction vintx; @@ -435,8 +437,7 @@ UniValue FinalizeCCV2Tx(bool remote, uint64_t mask, struct CCcontract_info* cp, int64_t utxovalues[CC_MAXVINS], change, totaloutputs = 0, totalinputs = 0; char destaddr[KOMODO_ADDRESS_BUFSIZE], myccaddr[KOMODO_ADDRESS_BUFSIZE], - globaladdr[KOMODO_ADDRESS_BUFSIZE], - mynftaddr[KOMODO_ADDRESS_BUFSIZE] = { '\0' }; + globaladdr[KOMODO_ADDRESS_BUFSIZE]; uint8_t myprivkey[32] = {'\0'}; //CC *cond = NULL, *probecond = NULL; UniValue sigData(UniValue::VARR), result(UniValue::VOBJ), partialConds(UniValue::VARR); @@ -445,7 +446,12 @@ UniValue FinalizeCCV2Tx(bool remote, uint64_t mask, struct CCcontract_info* cp, globalpk = GetUnspendable(cp, 0); _GetCCaddress(myccaddr, cp->evalcode, mypk, true); _GetCCaddress(globaladdr, cp->evalcode, globalpk, true); - GetTokensCCaddress(cp, mynftaddr, mypk, true); // get token or nft probe + + // mynftaddr[KOMODO_ADDRESS_BUFSIZE] = { '\0' }; + // GetTokensCCaddress(cp, mynftaddr, mypk, true); // get token or nft probe + std::vector tokenconds = GetTokenV2Conds(mypk); + std::vector tokenaddrs = GetTokenV2IndexKeys(mypk); + std::vector::iterator tokenit; n = mtx.vout.size(); for (int i = 0; i < n; i++) { @@ -474,10 +480,17 @@ UniValue FinalizeCCV2Tx(bool remote, uint64_t mask, struct CCcontract_info* cp, } else fprintf(stderr, "%s couldnt find %s mgret.%d\n", __func__, mtx.vin[i].prevout.hash.ToString().c_str(), mgret); } - if (!(mask & FINALIZECCTX_NO_CHANGE) && totalinputs >= totaloutputs + txfee) { - change = totalinputs - (totaloutputs + txfee); - if (!(mask & FINALIZECCTX_NO_CHANGE_WHEN_ZERO) || change > 0) - mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if (changeFlag != FINALIZECCTX_NO_CHANGE) { // no need change at all (already added by the caller itself) + CAmount change = totalinputs - (totaloutputs + txfee); + CTxOut changeVout(change, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG); + if (change >= 0) + { + if ((change != 0LL || changeFlag != FINALIZECCTX_NO_CHANGE_WHEN_ZERO) && // prevent adding zero change + (!changeVout.IsDust(::minRelayTxFee) || changeFlag != FINALIZECCTX_NO_CHANGE_WHEN_DUST)) // prevent adding dust change + { + mtx.vout.push_back(changeVout); + } + } } if (opret.size() > 0) mtx.vout.push_back(CTxOut(0, opret)); @@ -493,8 +506,10 @@ UniValue FinalizeCCV2Tx(bool remote, uint64_t mask, struct CCcontract_info* cp, if (vintx.vout[utxovout].scriptPubKey.IsPayToCryptoCondition() == 0) { if (KOMODO_NSPV_FULLNODE) { if (!remote) { - if (SignTx(mtx, i, vintx.vout[utxovout].nValue, vintx.vout[utxovout].scriptPubKey) == 0) + if (SignTx(mtx, i, vintx.vout[utxovout].nValue, vintx.vout[utxovout].scriptPubKey) == 0) { fprintf(stderr, "%s signing error for normal vini.%d\n", __func__, i); + return sigDataNull; + } } else { // if no myprivkey for mypk it means remote call from nspv superlite client // add sigData for superlite client @@ -512,6 +527,7 @@ UniValue FinalizeCCV2Tx(bool remote, uint64_t mask, struct CCcontract_info* cp, fprintf(stderr, "%s NSPV signing error for vini.%d\n", __func__, i); } } else { + bool bdontsign = false; Getscriptaddress(destaddr, vintx.vout[utxovout].scriptPubKey); if (strcmp(destaddr, globaladdr) == 0) { privkey = cp->CCpriv; @@ -519,55 +535,72 @@ UniValue FinalizeCCV2Tx(bool remote, uint64_t mask, struct CCcontract_info* cp, } else if (strcmp(destaddr, myccaddr) == 0) { privkey = myprivkey; cond.reset(MakeCCcond1(cp->evalcode, mypk)); - } else if (strcmp(destaddr, mynftaddr) == 0) { + //} else if (strcmp(destaddr, mynftaddr) == 0) { + } else if ((tokenit = std::find(tokenaddrs.begin(), tokenaddrs.end(), std::string(destaddr))) != tokenaddrs.end()) { privkey = myprivkey; - cond.reset(MakeTokensv2CCcond1(cp->evalcode, cp->evalcodeNFT, mypk)); + //cond.reset(MakeTokensv2CCcond1(cp->evalcode, mypk)); + cond = tokenconds[ std::distance(tokenaddrs.begin(), tokenit) ]; } else { const uint8_t nullpriv[32] = {'\0'}; + const uint8_t dontsign[32] = { 0xff }; // use vector of dest addresses and conds to probe vintxconds for (auto& t : cp->CCvintxprobes) { - char coinaddr[KOMODO_ADDRESS_BUFSIZE]; if (t.CCwrapped.get() != NULL) { - CCwrapper anonCond = t.CCwrapped; - CCtoAnon(anonCond.get()); - Getscriptaddress(coinaddr, CCPubKey(anonCond.get(), true)); - if (strcmp(destaddr, coinaddr) == 0) { - if (memcmp(t.CCpriv, nullpriv, sizeof(t.CCpriv) / sizeof(t.CCpriv[0])) != 0) - privkey = t.CCpriv; - else - privkey = myprivkey; - cond = t.CCwrapped; - break; + //CCwrapper anonCond = t.CCwrapped; + //CCtoAnon(anonCond.get()); // now in CCPubKey() + for (CC_SUBVER ccSubVer = CC_MIXED_MODE_SUBVER_0; ccSubVer <= CC_MIXED_MODE_SUBVER_MAX; ccSubVer = (CC_SUBVER)(ccSubVer+1)) + { + char coinaddr[KOMODO_ADDRESS_BUFSIZE]; + //Getscriptaddress(coinaddr, CCPubKey(anonCond.get(), ccSubVer)); + Getscriptaddress(coinaddr, CCPubKey(t.CCwrapped.get(), ccSubVer)); + if (strcmp(destaddr, coinaddr) == 0) { + if (memcmp(t.CCpriv, nullpriv, sizeof(t.CCpriv) / sizeof(t.CCpriv[0])) == 0) + privkey = myprivkey; + else if (memcmp(t.CCpriv, dontsign, sizeof(t.CCpriv) / sizeof(t.CCpriv[0])) == 0) + bdontsign = true; + else + privkey = t.CCpriv; + + cond = t.CCwrapped; + break; + } } + if (cond.get() != nullptr) break; // found cond } } } - if (cond.get() == NULL) { + if (cond.get() == nullptr) { fprintf(stderr, "%s vini.%d has CC signing error: could not find matching cond, address.(%s) %s\n", __func__, i, destaddr, EncodeHexTx(mtx).c_str()); memset(myprivkey, 0, sizeof(myprivkey)); return sigDataNull; } - if (!remote) // we have privkey in the wallet + if (bdontsign) { + mtx.vin[i].scriptSig = CCSig(cond.get()); // no signing cond + //std::cerr << __func__ << " using 'dont sign' vin" << i << std::endl; + } + else if (!remote) // we have privkey in the wallet { uint256 sighash = SignatureHash(CCPubKey(cond.get()), mtx, i, SIGHASH_ALL, utxovalues[i], consensusBranchId, &txdata); - if (cc_signTreeSecp256k1Msg32(cond.get(), privkey, sighash.begin()) != 0) { - std::string strcond; - cJSON *params = cc_conditionToJSON(cond.get()); - if (params) { - char *out = cJSON_PrintUnformatted(params); - cJSON_Delete(params); - if (out) { - strcond = out; - cJSON_free(out); - } - } + if (cc_signTreeSecp256k1Msg32(cond.get(), privkey, sighash.begin()) != 0 || + cc_signTreeSecp256k1HashMsg32(cond.get(), privkey, sighash.begin()) != 0) { - UniValue unicond(UniValue::VOBJ); - unicond.read(strcond); mtx.vin[i].scriptSig = CCSig(cond.get()); if (!IsCCInput(mtx.vin[i].scriptSig)) { // if fulfillment could not be serialised treat as signature threshold not reached // return partially signed condition: + std::string strcond; + cJSON *params = cc_conditionToJSON(cond.get()); + if (params) { + char *out = cJSON_PrintUnformatted(params); + cJSON_Delete(params); + if (out) { + strcond = out; + cJSON_free(out); + } + } + + UniValue unicond(UniValue::VOBJ); + unicond.read(strcond); UniValue elem(UniValue::VOBJ); elem.push_back(Pair("vin", i)); elem.push_back(Pair("ccaddress", destaddr)); @@ -698,6 +731,8 @@ UniValue AddSignatureCCTxV2(vuint8_t & vtx, const UniValue &jsonParams) // set cc or normal unspents from mempool static void AddCCunspentsInMempool(std::vector > &unspentOutputs, char *destaddr, bool isCC) { + if (!destaddr) return; + uint160 hashBytes; std::string addrstr(destaddr); CBitcoinAddress address(addrstr); @@ -713,44 +748,6 @@ static void AddCCunspentsInMempool(std::vector > addresses; addresses.push_back(std::make_pair(hashBytes, type)); mempool.getAddressIndex(addresses, memOutputs); - - //std::cerr << __func__ << " total memOutputs.size=" << memOutputs.size() << " hashBytes=" << hashBytes.GetHex() << " addrstr=" << addrstr << std::endl; - - // non indexed impl: - /* - for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) { - const CTransaction& memtx = mi->GetTx(); - for (int32_t i = 0; i < memtx.vout.size(); i++) - { - uint256 dummytxid; - int32_t dummyvout; - if (!myIsutxo_spentinmempool(dummytxid, dummyvout, memtx.GetHash(), i)) - { - if (isCC && memtx.vout[i].scriptPubKey.IsPayToCryptoCondition() || !isCC && !memtx.vout[i].scriptPubKey.IsPayToCryptoCondition()) - { - char voutaddr[64]; - Getscriptaddress(voutaddr, memtx.vout[i].scriptPubKey); - if (strcmp(voutaddr, destaddr) == 0) - { - // create unspent output key value pair - CAddressUnspentKey key; - CAddressUnspentValue value; - - key.type = type; - key.hashBytes = hashBytes; - key.txhash = memtx.GetHash(); - key.index = i; - - value.satoshis = memtx.vout[i].nValue; - value.blockHeight = 0; - value.script = memtx.vout[i].scriptPubKey; - unspentOutputs.push_back(std::make_pair(key, value)); - } - } - } - } - } - */ // impl using mempool address and spent indexes for (std::vector >::iterator mo = memOutputs.begin(); mo != memOutputs.end(); mo ++) @@ -790,12 +787,9 @@ void SetCCunspents(std::vector >::iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); ) - { - uint256 dummytxid; - int32_t dummyvout; - if (myIsutxo_spentinmempool(dummytxid, dummyvout, it->first.txhash, it->first.index)) { - //std::cerr << __func__ << " erasing spent in mempool txid=" << it->first.txhash.GetHex() << " index=" << it->first.index << " spenttxid=" << dummytxid.GetHex() << std::endl; - it = unspentOutputs.erase(it); - } - else - it++; - } */ AddCCunspentsInMempool(unspentOutputs, coinaddr, ccflag); } @@ -834,6 +816,9 @@ void SetCCunspentsCCIndex(std::vector > searchKeys; + + if (!coinaddr) + return; CBitcoinAddress address(coinaddr); if (address.GetIndexKey(hashBytes, type, true) == 0) @@ -848,66 +833,62 @@ void SetCCunspentsCCIndex(std::vector > &unspentOutputs, const char *coinaddr, uint256 creationId) { + if (!coinaddr) + return; CBitcoinAddress address( coinaddr ); uint160 hashBytes; int type; if (address.GetIndexKey(hashBytes, type, true)) { - mempool.getUnspentCCIndex({ std::make_pair(hashBytes, creationId) }, unspentOutputs); } } -void SetCCtxids(std::vector > &addressIndex,char *coinaddr,bool ccflag) +void SetAddressIndexOutputs(std::vector>& addressIndex, char* coinaddr, bool ccflag, int32_t beginHeight, int32_t endHeight) { - int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector > addresses; - if ( KOMODO_NSPV_SUPERLITE ) - { - NSPV_CCtxids(addressIndex,coinaddr,ccflag); + int32_t type = 0; + uint160 hashBytes; + std::vector> addresses; + if (KOMODO_NSPV_SUPERLITE) { + NSPV_CCindexOutputs(addressIndex, coinaddr, ccflag); return; } - n = (int32_t)strlen(coinaddr); - addrstr.resize(n+1); - ptr = (char *)addrstr.data(); - for (i=0; i<=n; i++) - ptr[i] = coinaddr[i]; - CBitcoinAddress address(addrstr); - if ( address.GetIndexKey(hashBytes, type, ccflag) == 0 ) + if (!coinaddr) return; - addresses.push_back(std::make_pair(hashBytes,type)); - for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) - { - if ( GetAddressIndex((*it).first, (*it).second, addressIndex) == 0 ) + CBitcoinAddress address(coinaddr); + if (address.GetIndexKey(hashBytes, type, ccflag) == 0) + return; + addresses.push_back(std::make_pair(hashBytes, type)); + for (std::vector>::iterator it = addresses.begin(); it != addresses.end(); it++) { + if (GetAddressIndex((*it).first, (*it).second, addressIndex, beginHeight, endHeight) == 0) return; } } -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, int64_t amount, uint256 filtertxid, uint8_t func) { - int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector > addresses; - std::vector > addressIndex; - if ( KOMODO_NSPV_SUPERLITE ) - { - NSPV_CCtxids(txids,coinaddr,ccflag,evalcode,filtertxid,func); + int32_t type = 0; + uint160 hashBytes; + std::vector> addresses; + std::vector> addressIndex; + if (KOMODO_NSPV_SUPERLITE) { + NSPV_CCtxids(txids, coinaddr, ccflag, evalcode, filtertxid, func); return; } - n = (int32_t)strlen(coinaddr); - addrstr.resize(n+1); - ptr = (char *)addrstr.data(); - for (i=0; i<=n; i++) - ptr[i] = coinaddr[i]; - CBitcoinAddress address(addrstr); - if ( address.GetIndexKey(hashBytes, type, ccflag) == 0 ) + if (!coinaddr) return; - addresses.push_back(std::make_pair(hashBytes,type)); - for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) - { - if ( GetAddressIndex((*it).first, (*it).second, addressIndex) == 0 ) + + CBitcoinAddress address(coinaddr); + if (address.GetIndexKey(hashBytes, type, ccflag) == 0) + return; + addresses.push_back(std::make_pair(hashBytes, type)); + for (std::vector>::iterator it = addresses.begin(); it != addresses.end(); it++) { + if (GetAddressIndex((*it).first, (*it).second, addressIndex) == 0) 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); + 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); } - } + } } int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout,int32_t CCflag) @@ -1062,9 +1043,10 @@ int64_t CCtoken_balanceV2(char *coinaddr,uint256 reftokenid) } // 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 CC_vinselect(int32_t *aboveip, CAmount *abovep, int32_t *belowip, CAmount *belowp, struct CC_utxo utxos[], int32_t numunspents, CAmount value) { - int32_t i,abovei,belowi; int64_t above,below,gap,atx_value; + int32_t i,abovei,belowi; + CAmount above,below,gap,atx_value; abovei = belowi = -1; for (above=below=i=0; i vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up; - if ( KOMODO_NSPV_SUPERLITE ) - return(NSPV_AddNormalinputs(mtx,mypk,total,maxinputs,&NSPV_U)); + if (hashBlock.IsNull()) + return 0; + AssertLockHeld(cs_main); + + // Find the block it claims to be in + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) + return 0; + return chainActive.Height() - pindex->GetHeight() + 1; +} - // if (mypk != pubkey2pk(Mypubkey())) //remote superlite mypk, do not use wallet since it is not locked for non-equal pks (see rpcs with nspv support)! - // return(AddNormalinputs3(mtx, mypk, total, maxinputs)); +static int CoinbaseGetBlocksToMaturity(const CTransaction &tx, uint256 hashBlock) +{ + if (!tx.IsCoinBase()) + return 0; + int32_t depth = MyGetDepthInMainChain(tx, hashBlock); + int32_t ut = tx.UnlockTime(0); + int32_t toMaturity = (ut - chainActive.Height()) < 0 ? 0 : ut - chainActive.Height(); + //printf("depth.%i, unlockTime.%i, toMaturity.%i\n", depth, ut, toMaturity); + ut = (COINBASE_MATURITY - depth) < 0 ? 0 : COINBASE_MATURITY - depth; + return(ut < toMaturity ? toMaturity : ut); +} + +CAmount AddNormalinputsLocal(CMutableTransaction& mtx, CPubKey mypk, CAmount total, int32_t maxinputs) +{ + int32_t abovei, belowi, ind, vout, n = 0; + CAmount sum, above, below; + CAmount remains, nValue, totalinputs = 0; + // CAmount threshold = 0LL; + uint256 txid, hashBlock; + std::vector vecOutputs; + CTransaction tx; + struct CC_utxo *utxos, *up; + if (KOMODO_NSPV_SUPERLITE) + return (NSPV_AddNormalinputs(mtx, mypk, total, maxinputs, &NSPV_U)); + + // if (mypk != pubkey2pk(Mypubkey())) //remote superlite mypk, do not use wallet since it is not locked for non-equal pks (see rpcs with nspv support)! + // return(AddNormalinputs3(mtx, mypk, total, maxinputs)); #ifdef ENABLE_WALLET assert(pwalletMain != NULL); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); - utxos = (struct CC_utxo *)calloc(CC_MAXVINS,sizeof(*utxos)); - if ( maxinputs > CC_MAXVINS ) + int64_t txLockTime = (int64_t)komodo_next_tx_locktime(); + pwalletMain->AvailableCoins(vecOutputs, false, NULL, true, txLockTime); + utxos = (struct CC_utxo*)calloc(CC_MAXVINS, sizeof(*utxos)); + if (maxinputs > CC_MAXVINS) maxinputs = CC_MAXVINS; - if ( maxinputs > 0 ) - threshold = total/maxinputs; - else threshold = total; - sum = 0; - BOOST_FOREACH(const COutput& out, vecOutputs) - { - if ( out.fSpendable != 0 && (vecOutputs.size() < maxinputs || out.tx->vout[out.i].nValue >= threshold) ) - { + /*if (maxinputs > 0) + threshold = total / maxinputs; + else + threshold = total;*/ + + + //TokelRemoveTimeLockedCoins(vecOutputs, txLockTime); + + sum = 0LL; + BOOST_FOREACH (const COutput& out, vecOutputs) { + if (out.fSpendable != 0 && (vecOutputs.size() < maxinputs || out.tx->vout[out.i].nValue > 0LL)) { // threshold not used as may lead to insufficient inputs messages txid = out.tx->GetHash(); vout = out.i; - if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 ) + if (myGetTransaction(txid, tx, hashBlock) != false && 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)out.tx->vout[out.i].nValue/COIN,n,maxutxos,txid.GetHex().c_str(),(int32_t)vout); - if ( mtx.vin.size() > 0 ) { - for (i=0; i 0) { + //std::cerr << __func__ << " skipping immature coinbase tx=" << txid.GetHex() << " COINBASE_MATURITY=" << COINBASE_MATURITY << std::endl; continue; + } } - if ( n > 0 ) - { - for (i=0; ivout[out.i].nValue/COIN,n,maxutxos,txid.GetHex().c_str(),(int32_t)vout); + if (mtx.vin.size() > 0) { + if (std::find_if(mtx.vin.begin(), mtx.vin.end(), [&](const CTxIn &vin){ return vin.prevout.hash == txid && vin.prevout.n == vout; }) != mtx.vin.end()) + continue; //already added } - if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) - { + if (n > 0) { + if (std::find_if(utxos, utxos+n, [&](const CC_utxo &utxo){ return utxo.txid == txid && utxo.vout == vout; }) != utxos+n) + continue; //already added + } + if (myIsutxo_spentinmempool(ignoretxid, ignorevin, txid, vout) == 0) { up = &utxos[n++]; up->txid = txid; up->nValue = out.tx->vout[out.i].nValue; up->vout = vout; sum += up->nValue; //fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxutxos); - if ( n >= maxinputs || sum >= total ) + if (n >= maxinputs || sum >= total) break; } } } } remains = total; - for (i=0; i0; i++) - { + for (int32_t i = 0; i < maxinputs && n > 0; i++) { below = above = 0; abovei = belowi = -1; - if ( CC_vinselect(&abovei,&above,&belowi,&below,utxos,n,remains) < 0 ) - { - printf("error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN); + if (CC_vinselect(&abovei, &above, &belowi, &below, utxos, n, remains) < 0) { + printf("error finding unspent i.%d of %d, %.8f vs %.8f\n", i, n, (double)remains / COIN, (double)total / COIN); free(utxos); - return(0); + return (0); } - if ( belowi < 0 || abovei >= 0 ) + if (belowi < 0 || abovei >= 0) ind = abovei; - else ind = belowi; - if ( ind < 0 ) - { - printf("error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind); + else + ind = belowi; + if (ind < 0) { + printf("error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n", i, n, (double)remains / COIN, (double)total / COIN, abovei, belowi, ind); free(utxos); - return(0); + return (0); } up = &utxos[ind]; - mtx.vin.push_back(CTxIn(up->txid,up->vout,CScript())); + mtx.vin.push_back(CTxIn(up->txid, up->vout, CScript(), (4294967295U-1))); // for TOKEL sequence non-final to allow CLTV spending totalinputs += up->nValue; remains -= up->nValue; utxos[ind] = utxos[--n]; - memset(&utxos[n],0,sizeof(utxos[n])); + memset(&utxos[n], 0, sizeof(utxos[n])); //fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs); - if ( totalinputs >= total || (i+1) >= maxinputs ) + if (totalinputs >= total || (i + 1) >= maxinputs) break; } free(utxos); - if ( totalinputs >= total ) - { + if (totalinputs >= total) { //fprintf(stderr,"return totalinputs %.8f\n",(double)totalinputs/COIN); - return(totalinputs); + return (totalinputs); } #endif - return(0); + return (0); } // always uses -pubkey param as mypk -int64_t AddNormalinputs2(CMutableTransaction &mtx, int64_t total, int32_t maxinputs) +CAmount AddNormalinputs2(CMutableTransaction &mtx, CAmount total, int32_t maxinputs) { CPubKey mypk = pubkey2pk(Mypubkey()); return AddNormalinputsRemote(mtx, mypk, total, maxinputs); } // has additional mypk param for nspv calls -int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t total, int32_t maxinputs, bool useMempool) +CAmount AddNormalinputsRemote(CMutableTransaction& mtx, CPubKey mypk, CAmount total, int32_t maxinputs, bool useMempool) { - int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; char coinaddr[64]; uint256 txid,hashBlock; CTransaction tx; struct CC_utxo *utxos,*up; - std::vector > unspentOutputs; - - if ( KOMODO_NSPV_SUPERLITE ) - return(NSPV_AddNormalinputs(mtx,mypk,total,maxinputs,&NSPV_U)); - utxos = (struct CC_utxo *)calloc(CC_MAXVINS,sizeof(*utxos)); - if ( maxinputs > CC_MAXVINS ) + int32_t abovei, belowi, ind, vout, n = 0; + CAmount sum, /*threshold,*/ above, below; + CAmount remains, nValue, totalinputs = 0; + char coinaddr[64]; + uint256 txid, hashBlock; + CTransaction tx; + struct CC_utxo *utxos, *up; + std::vector> unspentOutputs; + + if (KOMODO_NSPV_SUPERLITE) + return (NSPV_AddNormalinputs(mtx, mypk, total, maxinputs, &NSPV_U)); + utxos = (struct CC_utxo*)calloc(CC_MAXVINS, sizeof(*utxos)); + if (maxinputs > CC_MAXVINS) maxinputs = CC_MAXVINS; - if ( maxinputs > 0 ) - threshold = total/maxinputs; - else threshold = total; + /*if (maxinputs > 0) + threshold = total / maxinputs; + else + threshold = total;*/ sum = 0; - Getscriptaddress(coinaddr,CScript() << vscript_t(mypk.begin(), mypk.end()) << OP_CHECKSIG); + Getscriptaddress(coinaddr, CScript() << vscript_t(mypk.begin(), mypk.end()) << OP_CHECKSIG); if (!useMempool) - SetCCunspents(unspentOutputs,coinaddr,false); + SetCCunspents(unspentOutputs, coinaddr, false); else - SetCCunspentsWithMempool(unspentOutputs,coinaddr,false); - - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { + SetCCunspentsWithMempool(unspentOutputs, coinaddr, false); + + int64_t txLockTime = (int64_t)komodo_next_tx_locktime(); + + for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; //if ( it->second.satoshis < threshold ) // continue; // do not use threshold - if( it->second.satoshis == 0 ) - continue; //skip null outputs + if (it->second.satoshis == 0) + continue; //skip null outputs + + // if CLTV tx check it is spendable already + int64_t nLockTime; + if (it->second.script.IsCheckLockTimeVerify(&nLockTime) && !TokelCheckLockTimeHelper(nLockTime, txLockTime)) + continue; - if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 ) + 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); - if ( mtx.vin.size() > 0 ) { - for (i=0; i 0) { + //std::cerr << __func__ << " skipping immature coinbase tx=" << txid.GetHex() << " COINBASE_MATURITY=" << COINBASE_MATURITY << std::endl; continue; + } } - if ( n > 0 ) - { - for (i=0; i 0) { + if (std::find_if(mtx.vin.begin(), mtx.vin.end(), [&](const CTxIn &vin){ return vin.prevout.hash == txid && vin.prevout.n == vout; }) != mtx.vin.end()) + continue; //already added } - if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) - { + if (n > 0) { + if (std::find_if(utxos, utxos+n, [&](const CC_utxo &utxo){ return utxo.txid == txid && utxo.vout == vout; }) != utxos+n) + continue; //already added + } + if (myIsutxo_spentinmempool(ignoretxid, ignorevin, txid, vout) == 0) { up = &utxos[n++]; up->txid = txid; up->nValue = 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); - if ( n >= maxinputs || sum >= total ) + if (n >= maxinputs || sum >= total) break; } } } remains = total; - for (i=0; i0; i++) - { + for (int32_t i = 0; i < maxinputs && n > 0; i++) { below = above = 0; abovei = belowi = -1; - if ( CC_vinselect(&abovei,&above,&belowi,&below,utxos,n,remains) < 0 ) - { - printf("error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN); + if (CC_vinselect(&abovei, &above, &belowi, &below, utxos, n, remains) < 0) { + printf("error finding unspent i.%d of %d, %.8f vs %.8f\n", i, n, (double)remains / COIN, (double)total / COIN); free(utxos); - return(0); + return (0); } - if ( belowi < 0 || abovei >= 0 ) + if (belowi < 0 || abovei >= 0) ind = abovei; - else ind = belowi; - if ( ind < 0 ) - { - printf("error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind); + else + ind = belowi; + if (ind < 0) { + printf("error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n", i, n, (double)remains / COIN, (double)total / COIN, abovei, belowi, ind); free(utxos); - return(0); + return (0); } up = &utxos[ind]; - mtx.vin.push_back(CTxIn(up->txid,up->vout,CScript())); + mtx.vin.push_back(CTxIn(up->txid, up->vout, CScript(), (4294967295U-1))); // for TOKEL sequence non-final to allow CLTV spending totalinputs += up->nValue; remains -= up->nValue; utxos[ind] = utxos[--n]; - memset(&utxos[n],0,sizeof(utxos[n])); + memset(&utxos[n], 0, sizeof(utxos[n])); //fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs); - if ( totalinputs >= total || (i+1) >= maxinputs ) + if (totalinputs >= total || (i + 1) >= maxinputs) break; } free(utxos); - if ( totalinputs >= total ) - { + if (totalinputs >= total) { //fprintf(stderr,"return totalinputs %.8f\n",(double)totalinputs/COIN); - return(totalinputs); + return (totalinputs); } - return(0); + return (0); } -int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs,bool remote) +CAmount AddNormalinputs(CMutableTransaction& mtx, CPubKey mypk, CAmount total, int32_t maxinputs, bool remote) { - if (!remote) return (AddNormalinputsLocal(mtx,mypk,total,maxinputs)); - else return (AddNormalinputsRemote(mtx,mypk,total,maxinputs)); + if (!remote) + return (AddNormalinputsLocal(mtx, mypk, total, maxinputs)); + else + return (AddNormalinputsRemote(mtx, mypk, total, maxinputs)); } void AddSigData2UniValue(UniValue &sigdata, int32_t vini, UniValue& ccjson, std::string sscriptpubkey, int64_t amount) diff --git a/src/cc/CCupgrades.cpp b/src/cc/CCupgrades.cpp new file mode 100644 index 00000000000..7cc2c5f9bd2 --- /dev/null +++ b/src/cc/CCupgrades.cpp @@ -0,0 +1,136 @@ +/****************************************************************************** + * Copyright © 2022 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 "script/standard.h" +#include "CCupgrades.h" + +namespace CCUpgrades { + + class CUpgradesContainer { + + public: + // init protocol version for a chain if non-default + void init(const std::string &chainName, int nProtocolVersion) + { + assert(mChainUpgrades.find(chainName) == mChainUpgrades.end()); // mChainUpgrades[chainName] must be empty + mChainUpgrades[chainName].setActivationPoint(CCUPGID_ASSETS_INITIAL_CHAIN, 0LL, UPGRADE_ACTIVE, nProtocolVersion); + } + + // for a chain add time or height activation point and new protocol version + void addUpgradeActive(const std::string &chainName, UPGRADE_ID upgradeId, int64_t nTimeOrHeight, int nProtocolVersion) + { + assert(mChainUpgrades.find(chainName) != mChainUpgrades.end()); // mChainUpgrades[chainName] must be initialised + mChainUpgrades[chainName].setActivationPoint(upgradeId, nTimeOrHeight, UPGRADE_ACTIVE, nProtocolVersion); + } + public: + CUpgradesContainer() { + + // init a chain if you plan to upgrade it + init("TOKEL", CCOLDDEFAULT_PROTOCOL_VERSION); + init("TKLTEST", CCOLDDEFAULT_PROTOCOL_VERSION); + //init("DIMXY24", CCOLDDEFAULT_PROTOCOL_VERSION); + //init("DIMXY28", CCOLDDEFAULT_PROTOCOL_VERSION); + init("TKLTEST2", CCOLDDEFAULT_PROTOCOL_VERSION); + //init("DIMXY32", CCOLDDEFAULT_PROTOCOL_VERSION); + //init("DIMXY33", CCOLDDEFAULT_PROTOCOL_VERSION); + + // CCUPGID_ASSETS_OPDROP_VALIDATE_FIX activation + addUpgradeActive("TOKEL", CCUPGID_ASSETS_OPDROP_VALIDATE_FIX, CCASSETS_OPDROP_FIX_TOKEL_HEIGHT, CCOLDDEFAULT_PROTOCOL_VERSION); + addUpgradeActive("TKLTEST", CCUPGID_ASSETS_OPDROP_VALIDATE_FIX, CCASSETS_OPDROP_FIX_TKLTEST_HEIGHT, CCOLDDEFAULT_PROTOCOL_VERSION); + + // CCUPGID_MIXEDMODE_SUBVER_1 activation + addUpgradeActive("TOKEL", CCUPGID_MIXEDMODE_SUBVER_1, CCMIXEDMODE_SUBVER_1_TOKEL_TIMESTAMP, CCMIXEDMODE_SUBVER_1_PROTOCOL_VERSION); // bound to S6 NN HF + addUpgradeActive("TKLTEST", CCUPGID_MIXEDMODE_SUBVER_1, CCMIXEDMODE_SUBVER_1_TKLTEST_HEIGHT, CCMIXEDMODE_SUBVER_1_PROTOCOL_VERSION); + //addUpgradeActive("DIMXY24", CCUPGID_MIXEDMODE_SUBVER_1, CCMIXEDMODE_SUBVER_1_DIMXY24_HEIGHT, CCMIXEDMODE_SUBVER_1_PROTOCOL_VERSION); + //addUpgradeActive("DIMXY28", CCUPGID_MIXEDMODE_SUBVER_1, CCMIXEDMODE_SUBVER_1_DIMXY28_HEIGHT, CCMIXEDMODE_SUBVER_1_PROTOCOL_VERSION); + //addUpgradeActive("DIMXY32", CCUPGID_MIXEDMODE_SUBVER_1, CCMIXEDMODE_SUBVER_1_DIMXY32_HEIGHT, CCMIXEDMODE_SUBVER_1_PROTOCOL_VERSION); + addUpgradeActive("TKLTEST2", CCUPGID_MIXEDMODE_SUBVER_1, CCMIXEDMODE_SUBVER_1_TKLTEST2_HEIGHT, CCMIXEDMODE_SUBVER_1_PROTOCOL_VERSION); + //addUpgradeActive("DIMXY33", CCUPGID_MIXEDMODE_SUBVER_1, CCMIXEDMODE_SUBVER_1_DIMXY33_TIMESTAMP, CCMIXEDMODE_SUBVER_1_PROTOCOL_VERSION); + + // add more chains here... + // ... + } + + public: + std::map mChainUpgrades; + ChainUpgrades defaultUpgrades; + } ccChainsUpgrades; + + static const ChainUpgrades *pSelectedUpgrades = &ccChainsUpgrades.defaultUpgrades; + + // return ref to chain upgrades list by chain name: + void SelectUpgrades(const std::string &chainName) { + std::map::const_iterator it = ccChainsUpgrades.mChainUpgrades.find(chainName); + if (it != ccChainsUpgrades.mChainUpgrades.end()) { + pSelectedUpgrades = &it->second; + } + else { + pSelectedUpgrades = &ccChainsUpgrades.defaultUpgrades; + } + } + + const ChainUpgrades &GetUpgrades() + { + return *pSelectedUpgrades; + } + + void InitUpgrade(const std::string &chainName, int nProtocolVersion) + { + ccChainsUpgrades.init(chainName, nProtocolVersion); + } + + void AddUpgradeActive(const std::string &chainName, UPGRADE_ID upgradeId, int64_t nTimeOrHeight, int nProtocolVersion) + { + ccChainsUpgrades.addUpgradeActive(chainName, upgradeId, nTimeOrHeight, nProtocolVersion); + } + + bool IsUpgradeActive(int64_t nTime, int32_t nHeight, const ChainUpgrades &chainUpgrades, UPGRADE_ID id) { + if (chainUpgrades.mUpgrades.size() == 0) + return chainUpgrades.defaultUpgrade.status == UPGRADE_ACTIVE; + else { + std::map::const_iterator it = chainUpgrades.mUpgrades.find(id); + if (it != chainUpgrades.mUpgrades.end()) { + if (it->second.nActivationPoint >= LOCKTIME_THRESHOLD) // time + return nTime >= it->second.nActivationPoint ? it->second.status == UPGRADE_ACTIVE : false; + else // height + return nHeight >= it->second.nActivationPoint ? it->second.status == UPGRADE_ACTIVE : false; + } + return false; + } + } + + // get latest upgrade for current height or time + UpgradeInfo GetCurrentUpgradeInfo(int64_t nTime, int32_t nHeight, const ChainUpgrades &chainUpgrades) + { + if (chainUpgrades.mUpgrades.size() == 0) + return chainUpgrades.defaultUpgrade; + else { + UpgradeInfo current = chainUpgrades.mUpgrades.find(CCUPGID_ASSETS_INITIAL_CHAIN)->second; // set as initial chain info + for (auto const & upgr : chainUpgrades.mUpgrades) { // upgrades are ordered + if (upgr.second.nActivationPoint >= LOCKTIME_THRESHOLD) { // time + if (nTime >= upgr.second.nActivationPoint) + current = upgr.second; // find latest active upgrade + } else { // height + if (nHeight >= upgr.second.nActivationPoint) + current = upgr.second; // find latest active upgrade + } + } + return current; + } + } + +}; // namespace CCUpgrades diff --git a/src/cc/CCupgrades.h b/src/cc/CCupgrades.h new file mode 100644 index 00000000000..30b485b4245 --- /dev/null +++ b/src/cc/CCupgrades.h @@ -0,0 +1,91 @@ +/****************************************************************************** + * Copyright © 2014-2021 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 CC_UPGRADES_H +#define CC_UPGRADES_H + +#include +#include +#include +#include + +#include "version.h" +#include "komodo_defs.h" + +namespace CCUpgrades { + + // asset chain activation heights + const int64_t CCASSETS_OPDROP_FIX_TOKEL_HEIGHT = 286359; // 26 Nov + 90 days + const int64_t CCASSETS_OPDROP_FIX_TKLTEST_HEIGHT = 243159; // 26 Nov + 60 days + + const int64_t CCMIXEDMODE_SUBVER_1_TKLTEST_HEIGHT = 100000000; // TBD + const int64_t CCMIXEDMODE_SUBVER_1_DIMXY24_HEIGHT = 100000000; // TBD + const int64_t CCMIXEDMODE_SUBVER_1_DIMXY28_HEIGHT = 100000000; // TBD + const int64_t CCMIXEDMODE_SUBVER_1_DIMXY32_HEIGHT = 100000000; // TBD + const int64_t CCMIXEDMODE_SUBVER_1_TKLTEST2_HEIGHT = 89940; // approx 17 May 2022 10:00a.m. UTC + const int64_t CCMIXEDMODE_SUBVER_1_TOKEL_TIMESTAMP = nS6Timestamp; // bound to annual NN S6 update + const int64_t CCMIXEDMODE_SUBVER_1_DIMXY33_TIMESTAMP = 1653064202; // test HF 20 May 2022 + + // latest protocol version: + const int CCMIXEDMODE_SUBVER_1_PROTOCOL_VERSION = 170010; + // pre-upgrade protocol version: + const int CCOLDDEFAULT_PROTOCOL_VERSION = 170009; + const int CCNEWCHAIN_PROTOCOL_VERSION = CCMIXEDMODE_SUBVER_1_PROTOCOL_VERSION; + + enum UPGRADE_STATUS { + UPGRADE_ACTIVE = 1, + }; + + enum UPGRADE_ID { + CCUPGID_ASSETS_INITIAL_CHAIN = 0x00, + CCUPGID_ASSETS_OPDROP_VALIDATE_FIX = 0x01, + CCUPGID_MIXEDMODE_SUBVER_1 = 0x02, // new cc secp256k1 cond type and eval param, assets cc royalty fixes + }; + + struct UpgradeInfo { + int64_t nActivationPoint; + UPGRADE_STATUS status; + int nProtocolVersion; // used for disconnecting old nodes + }; + + class ChainUpgrades { + public: + ChainUpgrades() : defaultUpgrade({0, UPGRADE_ACTIVE, CCNEWCHAIN_PROTOCOL_VERSION}) { } + void setActivationPoint(UPGRADE_ID upgId, int64_t nTimeOrHeight, UPGRADE_STATUS upgStatus, int nProtocolVersion) { + mUpgrades[upgId] = { nTimeOrHeight, upgStatus, nProtocolVersion }; + } + + public: + std::map mUpgrades; + const UpgradeInfo defaultUpgrade; + }; + + /** init a protocol version for a chain (if it will be upgraded) */ + void InitUpgrade(const std::string &chainName, int nProtocolVersion); + /** for a chain add an upgrade activation point and new protocol version */ + void AddUpgradeActive(const std::string &chainName, UPGRADE_ID upgradeId, int64_t nTimeOrHeight, int nProtocolVersion); + /** select upgrades for a chain */ + void SelectUpgrades(const std::string &chainName); + /** get selected upgrades */ + const ChainUpgrades &GetUpgrades(); + /** check if an upgrade is active for selected upgrades */ + bool IsUpgradeActive(int64_t nTime, int32_t nHeight, const ChainUpgrades &chainUpgrades, UPGRADE_ID upgId); + /** get currently active upgrade for selected upgrades */ + UpgradeInfo GetCurrentUpgradeInfo(int64_t nTime, int32_t nHeight, const ChainUpgrades &chainUpgrades); + +}; // namespace CCUpgrades + +#endif // #ifndef CC_UPGRADES_H + diff --git a/src/cc/CCutilbits.cpp b/src/cc/CCutilbits.cpp index 13041873a25..fa7a120940f 100644 --- a/src/cc/CCutilbits.cpp +++ b/src/cc/CCutilbits.cpp @@ -20,6 +20,36 @@ #include "CCinclude.h" #include "komodo_structs.h" +bool fDisableCCLogForTests = false; + +// get address for a scriptPubKey +bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey) +{ + CTxDestination address; txnouttype whichType; + destaddr[0] = 0; + if ( scriptPubKey.begin() != 0 ) + { + if ( ExtractDestination(scriptPubKey,address) != 0 ) + { + strcpy(destaddr,(char *)CBitcoinAddress(address).ToString().c_str()); + return(true); + } + } + //fprintf(stderr,"ExtractDestination failed\n"); + return(false); +} + +// extract and compare addresses +bool IsEqualDestinations(const CScript &spk1, const CScript &spk2) +{ + char addr1[KOMODO_ADDRESS_BUFSIZE]; + char addr2[KOMODO_ADDRESS_BUFSIZE]; + if (Getscriptaddress(addr1, spk1) && Getscriptaddress(addr2, spk2)) + return strcmp(addr1, addr2) == 0; + else + return false; +} + int32_t unstringbits(char *buf,uint64_t bits) { int32_t i; @@ -104,6 +134,7 @@ CPubKey pubkey2pk(std::vector vpubkey) // 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) { + if (fDisableCCLogForTests) return false; if (level < 0) return true; // always print errors @@ -147,14 +178,3 @@ bool IsRemoteRPCCall() { return is_remote_rpc_call; } - -bool CCtoAnon(const CC* cond) -{ - for (int i = 0; i < cond->size; i++) - if (cc_typeId(cond->subconditions[i]) == CC_Threshold) { - CCwrapper tmp(cond->subconditions[i]); - cond->subconditions[i] = cc_anon(tmp.get()); - return (true); - } - return (false); -} \ No newline at end of file diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 6dd066f318f..daa2597106e 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -16,24 +16,14 @@ /* CCutils has low level functions that are universally useful for all contracts. */ - +#include "key_io.h" +#include "komodo_defs.h" +#include "komodo_structs.h" #include "CCinclude.h" #include "CCtokens.h" -#include "komodo_structs.h" -#include "key_io.h" - thread_local CCERROR CCerror = ""; -#ifdef TESTMODE - #define MIN_NON_NOTARIZED_CONFIRMS 2 -#else - #define MIN_NON_NOTARIZED_CONFIRMS 101 -#endif // TESTMODE -int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); -struct komodo_state *komodo_stateptr(char *symbol,char *dest); -extern uint32_t KOMODO_DPOWCONFS; - void endiancpy(uint8_t *dest,uint8_t *src,int32_t len) { int32_t i,j=0; @@ -151,8 +141,8 @@ CTxOut MakeCC1voutMixed(uint8_t evalcode,CAmount nValue, CPubKey pk, std::vector { CTxOut vout; CCwrapper payoutCond(MakeCCcond1(evalcode,pk)); - if (!CCtoAnon(payoutCond.get())) return (vout); - vout = CTxOut(nValue,CCPubKey(payoutCond.get(),true)); + //if (!CCtoAnon(payoutCond.get())) return (vout); + vout = CTxOut(nValue,CCPubKey(payoutCond.get(), CC_MIXED_MODE_SUBVER_0)); if ( vData ) { vout.scriptPubKey << *vData << OP_DROP; @@ -164,8 +154,8 @@ CTxOut MakeCC1of2voutMixed(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey p { CTxOut vout; CCwrapper payoutCond(MakeCCcond1of2(evalcode,pk1,pk2)); - if (!CCtoAnon(payoutCond.get())) return (vout); - vout = CTxOut(nValue,CCPubKey(payoutCond.get(),true)); + //if (!CCtoAnon(payoutCond.get())) return (vout); + vout = CTxOut(nValue,CCPubKey(payoutCond.get(), CC_MIXED_MODE_SUBVER_0)); if ( vData ) { vout.scriptPubKey << *vData << OP_DROP; @@ -249,21 +239,7 @@ void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, u strcpy(cp->tokens1of2addr, tokenaddr); } -bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey) -{ - CTxDestination address; txnouttype whichType; - destaddr[0] = 0; - if ( scriptPubKey.begin() != 0 ) - { - if ( ExtractDestination(scriptPubKey,address) != 0 ) - { - strcpy(destaddr,(char *)CBitcoinAddress(address).ToString().c_str()); - return(true); - } - } - //fprintf(stderr,"ExtractDestination failed\n"); - return(false); -} +// NOTE: bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey) moved to CCutilsbits.cpp to enable build komodo-tx tool bool GetCustomscriptaddress(char *destaddr,const CScript &scriptPubKey,uint8_t taddr,uint8_t prefix, uint8_t prefix2) { @@ -282,7 +258,7 @@ bool GetCCParams(Eval* eval, const CTransaction &tx, uint32_t nIn, { uint256 blockHash; - if (myGetTransaction(tx.vin[nIn].prevout.hash, txOut, blockHash) && txOut.vout.size() > tx.vin[nIn].prevout.n) + if (eval->GetTxUnconfirmed(tx.vin[nIn].prevout.hash, txOut, blockHash) && txOut.vout.size() > tx.vin[nIn].prevout.n) { CBlockIndex index; if (eval->GetBlock(blockHash, index)) @@ -401,8 +377,8 @@ bool _GetCCaddress(char *destaddr,uint8_t evalcode,CPubKey pk,bool mixed) destaddr[0] = 0; if (payoutCond.get() != 0 ) { - if (mixed) CCtoAnon(payoutCond.get()); - Getscriptaddress(destaddr,CCPubKey(payoutCond.get(),mixed)); + //if (mixed) CCtoAnon(payoutCond.get()); + Getscriptaddress(destaddr,CCPubKey(payoutCond.get(), mixed ? CC_MIXED_MODE_SUBVER_0 : CC_OLD_V1_SUBVER)); } return(destaddr[0] != 0); } @@ -426,9 +402,9 @@ static bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode1, uint8_t evalc destaddr[0] = 0; if (payoutCond != nullptr) { - if (mixed) - CCtoAnon(payoutCond.get()); - Getscriptaddress(destaddr, CCPubKey(payoutCond.get(), mixed)); + //if (mixed) + // CCtoAnon(payoutCond.get()); + Getscriptaddress(destaddr, CCPubKey(payoutCond.get(), mixed ? CC_MIXED_MODE_SUBVER_0 : CC_OLD_V1_SUBVER)); } return(destaddr[0] != 0); } @@ -439,7 +415,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, mixed)); + return(_GetTokensCCaddress(destaddr, cp->evalcode, 0, pk, mixed)); } bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubKey pk2, bool mixed) @@ -448,8 +424,8 @@ bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubK destaddr[0] = 0; if ( payoutCond.get() != 0 ) { - if (mixed) CCtoAnon(payoutCond.get()); - Getscriptaddress(destaddr,CCPubKey(payoutCond.get(),mixed)); + //if (mixed) CCtoAnon(payoutCond.get()); + Getscriptaddress(destaddr,CCPubKey(payoutCond.get(), mixed ? CC_MIXED_MODE_SUBVER_0 : CC_OLD_V1_SUBVER)); } return(destaddr[0] != 0); } @@ -458,16 +434,16 @@ bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey { CCwrapper payoutCond; if (!mixed) - payoutCond.reset(MakeTokensCCcond1of2(cp->evalcode, cp->evalcodeNFT, pk1, pk2)); + payoutCond.reset(MakeTokensCCcond1of2(cp->evalcode, pk1, pk2)); else - payoutCond.reset(MakeTokensv2CCcond1of2(cp->evalcode, cp->evalcodeNFT, pk1, pk2)); + payoutCond.reset(MakeTokensv2CCcond1of2(cp->evalcode, pk1, pk2)); destaddr[0] = 0; - if (payoutCond != nullptr) // if evalcodeNFT not set then it is dual-eval cc else three-eval cc + if (payoutCond != nullptr) { - if (mixed) - CCtoAnon(payoutCond.get()); - Getscriptaddress(destaddr, CCPubKey(payoutCond.get(), mixed)); + //if (mixed) + // CCtoAnon(payoutCond.get()); + Getscriptaddress(destaddr, CCPubKey(payoutCond.get(), mixed ? CC_MIXED_MODE_SUBVER_0 : CC_OLD_V1_SUBVER)); } return(destaddr[0] != 0); } @@ -619,7 +595,6 @@ bool Myprivkey(uint8_t myprivkey[]) 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; @@ -779,7 +754,8 @@ int32_t myGet_mempool_txs(std::vector &txs,uint8_t evalcode,uint8_ } return (NSPV_mempoolresult.numtxids); } - BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) + LOCK(mempool.cs); + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) { txs.push_back(e.GetTx()); i++; @@ -787,17 +763,20 @@ int32_t myGet_mempool_txs(std::vector &txs,uint8_t evalcode,uint8_ return(i); } -int32_t CCCointxidExists(char const *logcategory,uint256 txid, uint256 cointxid) +int32_t CCCointxidExists(char const* logcategory, uint256 txid, uint256 cointxid) { - char txidaddr[64]; std::string coin; int32_t numvouts; uint256 hashBlock; - std::vector > addressIndex; - CCtxidaddr_tweak(txidaddr,cointxid); - SetCCtxids(addressIndex,txidaddr,false); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - { - return(-1); + char txidaddr[64]; + std::string coin; + int32_t numvouts; + uint256 hashBlock; + + std::vector> addressIndex; + CCtxidaddr_tweak(txidaddr, cointxid); + SetAddressIndexOutputs(addressIndex, txidaddr, false); + for (std::vector>::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) { + return (-1); } - return(myIs_coinaddr_inmempoolvout(logcategory,txid,txidaddr)); + return (myIs_coinaddr_inmempoolvout(logcategory, txid, txidaddr)); } bool CompareHexVouts(std::string hex1, std::string hex2) @@ -842,94 +821,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() -{ - if ( KOMODO_NSPV_SUPERLITE ) - { - return (NSPV_inforesult.height); - } - else return chainActive.LastTip()->GetHeight(); -} - -bool komodo_txnotarizedconfirmed(uint256 txid, int32_t minconfirms) -{ - char str[65]; - int32_t confirms,minimumconfirms,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 ) - { - fprintf(stderr,"komodo_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str()); - return(0); - } - else if (txheight<=0) - { - fprintf(stderr,"komodo_txnotarizedconfirmed no txheight.%d for txid %s\n",txheight,txid.ToString().c_str()); - return(0); - } - else if (txheight>currentheight) - { - fprintf(stderr,"komodo_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,currentheight); - return(0); - } - confirms=1 + currentheight - txheight; - } - else - { - if ( myGetTransaction(txid,tx,hashBlock) == 0 ) - { - fprintf(stderr,"komodo_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str()); - return(0); - } - else if ( hashBlock == zeroid ) - { - fprintf(stderr,"komodo_txnotarizedconfirmed no hashBlock for txid %s\n",txid.ToString().c_str()); - return(0); - } - else if ( (pindex= komodo_blockindex(hashBlock)) == 0 || (txheight= pindex->GetHeight()) <= 0 ) - { - fprintf(stderr,"komodo_txnotarizedconfirmed no txheight.%d %p for txid %s\n",txheight,pindex,txid.ToString().c_str()); - return(0); - } - else if ( (pindex= chainActive.LastTip()) == 0 || pindex->GetHeight() < txheight ) - { - fprintf(stderr,"komodo_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,(int32_t)pindex->GetHeight()); - return(0); - } - 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) - return (true); - return (false); -} - CPubKey check_signing_pubkey(CScript scriptSig) { bool found = false; @@ -938,7 +829,7 @@ CPubKey check_signing_pubkey(CScript scriptSig) auto findEval = [](CC *cond, struct CCVisitor _) { bool r = false; - if (cc_typeId(cond) == CC_Secp256k1) { + if (!cc_isAnon(cond) && cc_typeId(cond) == CC_Secp256k1) { *(CPubKey*)_.context=buf2pk(cond->publicKey); r = true; } @@ -961,18 +852,20 @@ CPubKey check_signing_pubkey(CScript scriptSig) // returns total of normal inputs signed with this pubkey -int64_t TotalPubkeyNormalInputs(const CTransaction &tx, const CPubKey &pubkey) +CAmount TotalPubkeyNormalInputs(Eval *eval, const CTransaction &tx, const CPubKey &pubkey) { - int64_t total = 0; - for (auto vin : tx.vin) { + CAmount total = 0; + + for (const auto &vin : tx.vin) { CTransaction vintx; uint256 hashBlock; - if (!IsCCInput(vin.scriptSig) && myGetTransaction(vin.prevout.hash, vintx, hashBlock)) { + if (!IsCCInput(vin.scriptSig) && GetTxUnconfirmedOpt(eval, vin.prevout.hash, vintx, hashBlock)) { typedef std::vector valtype; std::vector vSolutions; txnouttype whichType; + bool iscltv; - if (Solver(vintx.vout[vin.prevout.n].scriptPubKey, whichType, vSolutions)) { + if (SolverCLTV(vintx.vout[vin.prevout.n].scriptPubKey, whichType, vSolutions, iscltv)) { switch (whichType) { case TX_PUBKEY: if (pubkey == CPubKey(vSolutions[0])) // is my input? @@ -990,17 +883,17 @@ int64_t TotalPubkeyNormalInputs(const CTransaction &tx, const CPubKey &pubkey) } // returns total of CC inputs signed with this pubkey -int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey) +CAmount TotalPubkeyCCInputs(Eval *eval, const CTransaction &tx, const CPubKey &pubkey) { - int64_t total = 0; - for (auto vin : tx.vin) { + CAmount total = 0; + for (const auto &vin : tx.vin) { if (IsCCInput(vin.scriptSig)) { CPubKey vinPubkey = check_signing_pubkey(vin.scriptSig); if (vinPubkey.IsValid()) { if (vinPubkey == pubkey) { CTransaction vintx; uint256 hashBlock; - if (myGetTransaction(vin.prevout.hash, vintx, hashBlock)) { + if (GetTxUnconfirmedOpt(eval, vin.prevout.hash, vintx, hashBlock)) { total += vintx.vout[vin.prevout.n].nValue; } } @@ -1010,18 +903,18 @@ int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey) return total; } -bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector paramsNull,const CTransaction &ctx, unsigned int nIn, std::shared_ptr evalcodeChecker) +bool ProcessCC(struct CCcontract_info* cp, Eval* eval, std::vector paramsNull, const CTransaction& ctx, unsigned int nIn, std::shared_ptr evalcodeChecker) { - CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t height,i,n,from_mempool = 0; int64_t amount; std::vector origpubkey; + int32_t height; + //int32_t from_mempool = 0; height = KOMODO_CONNECTING; - if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation - return(true); - if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE ) + if (KOMODO_CONNECTING < 0) // always comes back with > 0 for final confirmation + return true; + if (ASSETCHAINS_CC == 0 || (height & ~(1 << 30)) < KOMODO_CCACTIVATE) return eval->Invalid("CC are disabled or not active yet"); - if ( (KOMODO_CONNECTING & (1<<30)) != 0 ) - { - from_mempool = 1; - height &= ((1<<30) - 1); + if ((KOMODO_CONNECTING & (1 << 30)) != 0) { + //from_mempool = 1; + height &= ((1 << 30) - 1); } if (cp->validate == NULL) return eval->Invalid("validation not supported for eval code"); @@ -1033,70 +926,98 @@ bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector param // return(true); //fprintf(stderr,"process CC %02x\n",cp->evalcode); CCclearvars(cp); - if ( paramsNull.size() != 0 ) // Don't expect params - return eval->Invalid("Cannot have params"); + if (paramsNull.size() != 0) // Don't expect params + return eval->Invalid("eval conds cannot have params yet"); //else if ( ctx.vout.size() == 0 ) // spend can go to z-addresses // return eval->Invalid("no-vouts"); - else if ( (*cp->validate)(cp,eval,ctx,nIn) != 0 ) - { + else if ((*cp->validate)(cp, eval, ctx, nIn) != 0) { //fprintf(stderr,"done CC %02x\n",cp->evalcode); //cp->prevtxid = txid; - if (evalcodeChecker.get()!=NULL) evalcodeChecker->MarkEvalCode(ctx.GetHash(),cp->evalcode); - return(true); + if (evalcodeChecker.get() != NULL) + evalcodeChecker->MarkEvalCode(ctx.GetHash(), cp->evalcode); + return true; } //fprintf(stderr,"invalid CC %02x\n",cp->evalcode); - return(false); + return false; } -extern struct CCcontract_info CCinfos[0x100]; -extern std::string MYCCLIBNAME; -bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx,unsigned int nIn); - -bool CClib_Dispatch(const CC *cond,Eval *eval,std::vector paramsNull,const CTransaction &txTo,unsigned int nIn,std::shared_ptr evalcodeChecker) +bool SubcallCCValidate(Eval* eval, uint8_t evalcode, const CTransaction& ctx, int32_t nIn) { - uint8_t evalcode; int32_t height,from_mempool; struct CCcontract_info *cp; - if ( ASSETCHAINS_CCLIB != MYCCLIBNAME ) + if (ASSETCHAINS_CC == 0) + return eval->Invalid("CC are disabled"); + + + if ( ASSETCHAINS_CCDISABLES[evalcode] != 0 ) + { + // check if a height activation has been set. + if ( mapHeightEvalActivate[evalcode] == 0 || eval->GetCurrentHeight() == 0 || mapHeightEvalActivate[evalcode] > eval->GetCurrentHeight() ) + { + return eval->Invalid("disabled-code, -ac_ccenables didnt include this ecode"); + } + } + + struct CCcontract_info *cp; + cp = &CCinfos[(int32_t)evalcode]; + if ( cp->didinit == 0 ) { - fprintf(stderr,"-ac_cclib=%s vs myname %s\n",ASSETCHAINS_CCLIB.c_str(),MYCCLIBNAME.c_str()); + CCinit(cp, evalcode); + cp->didinit = 1; + } + + if (cp->validate == NULL) + return eval->Invalid("validation not supported for eval code"); + + CCclearvars(cp); + if ((*cp->validate)(cp, eval, ctx, nIn) != false) { + return true; + } + else { + return false; + } +} + +bool CClib_Dispatch(const CC* cond, Eval* eval, std::vector paramsNull, const CTransaction& txTo, unsigned int nIn, std::shared_ptr evalcodeChecker) +{ + uint8_t evalcode; + int32_t height, from_mempool; + struct CCcontract_info* cp; + if (ASSETCHAINS_CCLIB != MYCCLIBNAME) { + fprintf(stderr, "-ac_cclib=%s vs myname %s\n", ASSETCHAINS_CCLIB.c_str(), MYCCLIBNAME.c_str()); return eval->Invalid("-ac_cclib name mismatches myname"); } height = KOMODO_CONNECTING; - if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation - return(true); - if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE ) + if (KOMODO_CONNECTING < 0) // always comes back with > 0 for final confirmation + return (true); + if (ASSETCHAINS_CC == 0 || (height & ~(1 << 30)) < KOMODO_CCACTIVATE) return eval->Invalid("CC are disabled or not active yet"); - if ( (KOMODO_CONNECTING & (1<<30)) != 0 ) - { + if ((KOMODO_CONNECTING & (1 << 30)) != 0) { from_mempool = 1; - height &= ((1<<30) - 1); + height &= ((1 << 30) - 1); } evalcode = cond->code[0]; - if (evalcodeChecker.get()!=NULL && evalcodeChecker->CheckEvalCode(txTo.GetHash(),evalcode)!=0) return true; - if ( evalcode >= EVAL_FIRSTUSER && evalcode <= EVAL_LASTUSER ) - { + if (evalcodeChecker.get() != NULL && evalcodeChecker->CheckEvalCode(txTo.GetHash(), evalcode) != 0) + return true; + if (evalcode >= EVAL_FIRSTUSER && evalcode <= EVAL_LASTUSER) { cp = &CCinfos[(int32_t)evalcode]; - if ( cp->didinit == 0 ) - { - if ( CClib_initcp(cp,evalcode) == 0 ) + if (cp->didinit == 0) { + if (CClib_initcp(cp, evalcode) == 0) cp->didinit = 1; - else return eval->Invalid("unsupported CClib evalcode"); + else + return eval->Invalid("unsupported CClib evalcode"); } CCclearvars(cp); - if ( paramsNull.size() != 0 ) // Don't expect params + if (paramsNull.size() != 0) // Don't expect params return eval->Invalid("Cannot have params"); - else if ( CClib_validate(cp,height,eval,txTo,nIn) != 0 ) - { - if (evalcodeChecker.get()!=NULL) evalcodeChecker->MarkEvalCode(txTo.GetHash(),evalcode); - return(true); + else if (CClib_validate(cp, height, eval, txTo, nIn) != 0) { + if (evalcodeChecker.get() != NULL) + evalcodeChecker->MarkEvalCode(txTo.GetHash(), evalcode); + return (true); } - return(false); //eval->Invalid("error in CClib_validate"); + return (false); //eval->Invalid("error in CClib_validate"); } 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); @@ -1198,54 +1119,6 @@ uint8_t *SuperNET_ciphercalc(uint8_t **ptrp,int32_t *cipherlenp,bits256 privkey, return(origptr); } -uint8_t *komodo_DEX_encrypt(uint8_t **allocatedp,uint8_t *data,int32_t *datalenp,bits256 destpubkey,bits256 privkey) -{ - int32_t cipherlen; uint8_t *cipher; - cipher = SuperNET_ciphercalc(allocatedp,&cipherlen,privkey,destpubkey,data,*datalenp); - *datalenp = cipherlen; - return(cipher); -} - -uint8_t *komodo_DEX_decrypt(uint8_t *senderpub,uint8_t **allocatedp,uint8_t *data,int32_t *datalenp,bits256 privkey) -{ - int32_t msglen; - *allocatedp = 0; - if ( (msglen= *datalenp) <= crypto_box_NONCEBYTES + crypto_box_ZEROBYTES + sizeof(bits256) ) - { - *datalenp = 0; - return(0); - } - if ( (data= SuperNET_deciphercalc(senderpub,allocatedp,&msglen,privkey,data,*datalenp)) == 0 ) - { - //printf("komodo_DEX_decrypt decrytion error\n"); - *datalenp = 0; - return(0); - } else *datalenp = msglen; - return(data); -} - -void komodo_DEX_privkey(bits256 &privkey) -{ - bits256 priv,hash; - Myprivkey(priv.bytes); - vcalc_sha256(0,hash.bytes,priv.bytes,32); - vcalc_sha256(0,privkey.bytes,hash.bytes,32); - memset(priv.bytes,0,sizeof(priv)); - memset(hash.bytes,0,sizeof(hash)); -} - -void komodo_DEX_pubkey(bits256 &pubkey) -{ - bits256 privkey; - komodo_DEX_privkey(privkey); - /*{ - char *bits256_str(char hexstr[65],bits256 x); - char str[65]; - fprintf(stderr,"new DEX_privkey %s\n",bits256_str(str,privkey)); - }*/ - pubkey = curve25519(privkey,curve25519_basepoint9()); - memset(privkey.bytes,0,sizeof(privkey)); -} // add probe vintx conditions for making CCSig in FinalizeCCTx void CCAddVintxCond(struct CCcontract_info *cp, const CCwrapper &condWrapped, const uint8_t *priv) @@ -1480,10 +1353,11 @@ UniValue OracleFormat(uint8_t *data,int32_t datalen,char *format,int32_t formatl // get OP_DROP data: -bool GetCCDropAsOpret(const CScript &scriptPubKey, CScript &opret) +CScript GetCCDropAsOpret(const CScript &scriptPubKey) { std::vector> vParams; CScript dummy; + CScript opret; if (scriptPubKey.IsPayToCryptoCondition(&dummy, vParams)) { @@ -1494,11 +1368,12 @@ bool GetCCDropAsOpret(const CScript &scriptPubKey, CScript &opret) LOGSTREAMFN("ccutils", CCLOG_DEBUG1, stream << " evalcode=" << (int)parsed.evalCode << " vKeys.size()=" << (int)parsed.vKeys.size() << " vData.size()=" << (int)parsed.vData.size() << std::endl); if (parsed.vData.size() > 0) { opret << OP_RETURN << parsed.vData[0]; // return vData[0] as cc opret - return true; + return opret; } } - /*if (vParams.size() >= 1) // allow more data after cc opret + /* parse OP_DROP without verus header (such opdrops are not supported anymore): + if (vParams.size() >= 1) // allow more data after cc opret { //uint8_t version; //uint8_t evalCode; @@ -1523,10 +1398,9 @@ bool GetCCDropAsOpret(const CScript &scriptPubKey, CScript &opret) opret << OP_RETURN << vParams[0]; // no verus header, treat vParams[0] as cc data and return as opret return true; } - } - */ + } */ } - return false; + return CScript(); } // get OP_DROP data for mixed cc vouts @@ -1626,27 +1500,31 @@ UniValue CCaddress(struct CCcontract_info *cp, const char *name, const std::vect // return funcid, version and creationid bool CCDecodeTxVout(const CTransaction &tx, int32_t n, uint8_t &evalcode, uint8_t &funcid, uint8_t &version, uint256 &creationId) { - CScript opdrop; - vscript_t ccdata; - if (tx.vout.size() > 0) { // note: assumes that this is a cc vout (does not check this) // first try if OP_DROP data exists bool usedOpreturn; - if (GetCCDropAsOpret(tx.vout[n].scriptPubKey, opdrop)) - GetOpReturnData(opdrop, ccdata), usedOpreturn = false; - else - GetOpReturnData(tx.vout.back().scriptPubKey, ccdata), usedOpreturn = true; // use OP_RETURN in the last vout if no OP_DROP data + CScript opdrop; + vscript_t vccdata; + + if (!(opdrop = GetCCDropAsOpret(tx.vout[n].scriptPubKey)).empty()) { + GetOpReturnData(opdrop, vccdata); + usedOpreturn = false; + } + else { + GetOpReturnData(tx.vout.back().scriptPubKey, vccdata); + usedOpreturn = true; // use OP_RETURN in the last vout if no OP_DROP data + } // use following algotithm to determine creationId // get the evalcode from ccdata // if no cc vins found with this evalcode this is the creation tx and creationId = tx.GetHash() // else the creationId is after the version field: 'evalcode funcid version creationId' - if (ccdata.size() >= 3) { + if (vccdata.size() >= 3) { struct CCcontract_info *cp, C; - cp = CCinit(&C, ccdata[0]); + cp = CCinit(&C, vccdata[0]); int32_t i = 0; for (; i < tx.vin.size(); i ++) if (cp->ismyvin(tx.vin[i].scriptSig)) @@ -1654,17 +1532,17 @@ bool CCDecodeTxVout(const CTransaction &tx, int32_t n, uint8_t &evalcode, uint8_ if (i == tx.vin.size()) { creationId = tx.GetHash(); // tx is the creation tx - evalcode = ccdata[0]; - funcid = ccdata[1]; - version = ccdata[2]; + evalcode = vccdata[0]; + funcid = vccdata[1]; + version = vccdata[2]; LOGSTREAMFN("ccutils", CCLOG_DEBUG1, stream << " evalcode=" << (int)evalcode << " funcid=" << (char)funcid << "(" << (int)funcid << "), version=" << (int)version << std::endl); } else { uint256 encodedCrid; - if (ccdata.size() >= 3 + sizeof(uint256)) { // get creationId from the ccdata + if (vccdata.size() >= 3 + sizeof(uint256)) { // get creationId from the ccdata bool isEof = true; - if (!E_UNMARSHAL(ccdata, ss >> evalcode; ss >> funcid; ss >> version; ss >> encodedCrid; isEof = ss.eof()) && isEof) { // E_UNMARSHAL might parse okay but return false if not EoF yet. So EoF==true means bad parse + if (!E_UNMARSHAL(vccdata, ss >> evalcode; ss >> funcid; ss >> version; ss >> encodedCrid; isEof = ss.eof()) && isEof) { // E_UNMARSHAL might parse okay but return false if not EoF yet. So EoF==true means bad parse LOGSTREAMFN("ccutils", CCLOG_DEBUG1, stream << "failed to decode ccdata, isEof=" << isEof << " usedOpreturn=" << usedOpreturn << " tx=" << HexStr(E_MARSHAL(ss << tx)) << std::endl); return false; } @@ -1676,4 +1554,90 @@ bool CCDecodeTxVout(const CTransaction &tx, int32_t n, uint8_t &evalcode, uint8_ return true; } return false; +} + +bool IsBlockHashInActiveChain(uint256 hashBlock) +{ + AssertLockHeld(cs_main); + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) { + CBlockIndex* pindex = (*mi).second; + if (chainActive.Contains(pindex)) + return true; + } + return false; +} + +/** + * IsTxidInActiveChain load a tx and checks if it is in active chain (not in orphaned blocks) + * cs_main section should be locked + */ +bool IsTxidInActiveChain(uint256 txid) +{ + CTransaction tx; + uint256 hashBlock; + + if (myGetTransaction(txid, tx, hashBlock)) + { + return IsBlockHashInActiveChain(hashBlock); + } + return false; +} + +// decode CC mixed mode to UniValue and specially process anon sec256hash +UniValue CCDecodeMixedMode(const CC *cond) +{ + UniValue result(UniValue::VOBJ); + + auto decodeCond = [](const CC *cond) -> UniValue + { + UniValue uCond(UniValue::VOBJ); + uCond.pushKV("type", cc_typeName(cond)); + if (!cc_isAnon(cond)) + { + uCond.pushKV("isAnon", "no"); + if (cc_typeId(cond) == CC_Eval) + uCond.pushKV("EvalCode", EvalToStr(cond->code[0])); + else if(cc_typeId(cond) == CC_Threshold) { + uCond.pushKV("threshold", cond->size); + uCond.pushKV("subconditions", CCDecodeMixedMode(cond)); + } + } + else + { + uCond.pushKV("isAnon", "yes"); + + if (cc_typeId(cond) == CC_Secp256k1hash) { + std::string fingerprintHex = HexStr(cond->fingerprint, cond->fingerprint + sizeof(uint160)); + CKeyID keyid(uint160(vuint8_t(cond->fingerprint, cond->fingerprint + sizeof(uint160)))); + CBitcoinAddress addr; + addr.Set(keyid); + uCond.pushKV("destination", addr.ToString()); + } + else { + uCond.pushKV("fingerprint", HexStr(cond->fingerprint, cond->fingerprint + sizeof(cond->fingerprint))); + if (cc_typeId(cond) == CC_Threshold) + uCond.pushKV("subtypes", (int64_t)cond->subtypes); + } + } + return uCond; + }; + + if (!cc_isAnon(cond) && cc_typeId(cond) == CC_Threshold) { + UniValue uThreshold(UniValue::VOBJ); + UniValue uSubConds(UniValue::VARR); + for (int i = 0; i < cond->size; i ++) { + UniValue uSubCond = decodeCond(cond->subconditions[i]); + uSubConds.push_back(uSubCond); + } + uThreshold.pushKV("type", cc_typeName(cond)); + uThreshold.pushKV("size", cond->size); + uThreshold.pushKV("subconditions", uSubConds); + return uThreshold; + } + else + { + UniValue uCond = decodeCond(cond); + return uCond; + } } \ No newline at end of file diff --git a/src/cc/Makefile b/src/cc/Makefile deleted file mode 100644 index 3e988f27935..00000000000 --- a/src/cc/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -SHELL = /bin/sh -CC = gcc -CC_DARWIN = g++-6 -CC_WIN = x86_64-w64-mingw32-gcc-posix -CFLAGS_DARWIN = -std=c++11 -arch x86_64 -I/usr/local/Cellar/gcc\@6/6.4.0_2/include/c++/6.4.0/ -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -c -Wl,-undefined -Wl,dynamic_lookup -dynamiclib -CFLAGS = -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -CFLAGS_WIN = -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -DEBUGFLAGS = -O0 -D _DEBUG -RELEASEFLAGS = -O2 -D NDEBUG -combine -fwhole-program -$(info $(OS)) -OS := $(shell uname -s) -$(info $(OS)) -TARGET = ../libcc.so -TARGET_DARWIN = ../libcc.dylib -TARGET_WIN = ../libcc.dll -SOURCES = cclib.cpp -#HEADERS = $(shell echo ../cryptoconditions/include/*.h) - -all: $(TARGET) - -$(TARGET): $(SOURCES) - $(info Building cclib to src/) -ifeq ($(OS),Darwin) - $(CC_DARWIN) $(CFLAGS_DARWIN) $(DEBUGFLAGS) -o $(TARGET_DARWIN) $(SOURCES) -else ifeq ($(OS),Linux) - $(CC) $(CFLAGS) $(DEBUGFLAGS) -o $(TARGET) $(SOURCES) -#else ifeq ($(WIN_HOST),True) - todo: pass ENV var from build.sh if WIN host -else - $(info WINDOWS) - $(CC_WIN) $(CFLAGS_WIN) $(DEBUGFLAGS) -o $(TARGET_WIN) $(SOURCES) -endif - -clean: - rm -rf $(TARGET) diff --git a/src/cc/Makefile_custom b/src/cc/Makefile_custom index 4391a106039..bfc1b336175 100755 --- a/src/cc/Makefile_custom +++ b/src/cc/Makefile_custom @@ -1,8 +1,8 @@ SHELL = /bin/sh -CC = gcc +CC = g++ CC_DARWIN = g++-8 -CC_WIN = x86_64-w64-mingw32-gcc-posix -CFLAGS_DARWIN = -DBUILD_CUSTOMCC -std=c++11 -arch x86_64 -I../secp256k1/include -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -Wl,-undefined -Wl,dynamic_lookup -Wno-write-strings -shared -dynamiclib +CC_WIN = x86_64-w64-mingw32-g++-posix +CFLAGS_DARWIN = -DBUILD_CUSTOMCC -std=c++11 -arch x86_64 -I../secp256k1/include -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -Wl,-undefined -Wl,dynamic_lookup -Wno-write-strings -shared -dynamiclib CFLAGS = -Wno-write-strings -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared CFLAGS_WIN = -Wno-write-strings -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../../depends/x86_64-w64-mingw32/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared DEBUGFLAGS = -O0 -D _DEBUG @@ -10,29 +10,25 @@ RELEASEFLAGS = -O2 -D NDEBUG -combine -fwhole-program $(info $(OS)) OS := $(shell uname -s) $(info $(OS)) -TARGET = customcc.so -TARGET_DARWIN = customcc.dylib -TARGET_WIN = customcc.dll +$(info $(HOST)) +TARGET = libcc.a SOURCES = cclib.cpp #HEADERS = $(shell echo ../cryptoconditions/include/*.h) -I/usr/local/Cellar/gcc\@8/8.3.0/include/c++/8.3.0/ -HEADERS = CCinclude.h CCtokens.h +HEADERS = *.h all: $(TARGET) $(TARGET): $(SOURCES) $(HEADERS) $(info Building cclib to src/) ifeq ($(OS),Darwin) - $(CC_DARWIN) $(CFLAGS_DARWIN) $(DEBUGFLAGS) -o $(TARGET_DARWIN) -c $(SOURCES) - cp $(TARGET_DARWIN) ../libcc.dylib + $(CC_DARWIN) $(CFLAGS_DARWIN) $(DEBUGFLAGS) -o $(TARGET) -c $(SOURCES) else ifeq ($(HOST),x86_64-w64-mingw32) $(info WINDOWS) - $(CC_WIN) $(CFLAGS_WIN) $(DEBUGFLAGS) -o $(TARGET_WIN) -c $(SOURCES) - cp $(TARGET_WIN) ../libcc.dll + $(CC_WIN) $(CFLAGS_WIN) $(DEBUGFLAGS) -o $(TARGET) -c $(SOURCES) #else ifeq ($(WIN_HOST),True) - todo: pass ENV var from build.sh if WIN host else $(info LINUX) $(CC) $(CFLAGS) $(DEBUGFLAGS) -o $(TARGET) -c $(SOURCES) - cp $(TARGET) ../libcc.so endif clean: diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp index 30c9840ec06..ad23f894bcd 100644 --- a/src/cc/assets.cpp +++ b/src/cc/assets.cpp @@ -17,7 +17,12 @@ #include "CCassets.h" #include "CCtokens_impl.h" #include "CCassetsCore_impl.h" -#include "CCNFTData.h" +#include "CCTokelData.h" +#include "CCupgrades.h" + +const int32_t CCVOUT = 1; +const int32_t NORMALVOUT = 0; + /* TODO: update: @@ -160,31 +165,31 @@ static bool AssetsValidateInternal(struct CCcontract_info *cp, Eval* eval,const static uint256 zero; CTxDestination address; CTransaction vinTx, createTx; - uint256 hashBlock, assetid, assetid2; - //int32_t preventCCvins, preventCCvouts; + uint256 hashBlock, assetid; int32_t ccvins = -1, ccvouts = -1; - int64_t unit_price, vin_unit_price; - vuint8_t vorigpubkey, vin_origpubkey, vopretNonfungible; + CAmount unit_price, vin_unit_price; + vuint8_t vorigpubkey, vin_origpubkey, vextraData; TokenDataTuple tokenData; uint8_t funcid, evalCodeInOpret; - char destaddr[KOMODO_ADDRESS_BUFSIZE], origNormalAddr[KOMODO_ADDRESS_BUFSIZE], ownerNormalAddr[KOMODO_ADDRESS_BUFSIZE]; + char destaddr[KOMODO_ADDRESS_BUFSIZE], origNormalAddr[KOMODO_ADDRESS_BUFSIZE], tokenCreatorNormalAddr[KOMODO_ADDRESS_BUFSIZE]; char origTokensCCaddr[KOMODO_ADDRESS_BUFSIZE], origCCaddrDummy[KOMODO_ADDRESS_BUFSIZE]; - char tokensDualEvalUnspendableCCaddr[KOMODO_ADDRESS_BUFSIZE], origAssetsCCaddr[KOMODO_ADDRESS_BUFSIZE]; + char tokensDualEvalUnspendableCCaddr[KOMODO_ADDRESS_BUFSIZE], origAssetsCCaddr[KOMODO_ADDRESS_BUFSIZE], globalAssetsCCaddr[KOMODO_ADDRESS_BUFSIZE]; + char markerCCaddress[KOMODO_ADDRESS_BUFSIZE]; + int32_t expiryHeight, vin_expiryHeight; + std::vector::const_iterator ccvin, markervin; CAmount vin_tokens = 0; - CAmount vin_assetoshis = 0; + CAmount vin_coins = 0; - int32_t numvins = tx.vin.size(); - int32_t numvouts = tx.vout.size(); - //preventCCvins = preventCCvouts = -1; - + if (tx.vin.size() == 0) + return eval->Invalid("AssetValidate: no vins"); if (tx.vout.size() == 0) return eval->Invalid("AssetValidate: no vouts"); - if((funcid = A::DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, unit_price, vorigpubkey)) == 0 ) + if((funcid = A::DecodeAssetTokenOpRet(tx.vout.back().scriptPubKey, evalCodeInOpret, assetid, unit_price, vorigpubkey, expiryHeight)) == 0 ) return eval->Invalid("AssetValidate: invalid opreturn payload"); - // reinit cpAssets as we could set evalcodeNFT in it + // reinit cpAssets for a chance anything is set there struct CCcontract_info *cpAssets, assetsC; cpAssets = CCinit(&assetsC, A::EvalCode()); @@ -193,27 +198,34 @@ static bool AssetsValidateInternal(struct CCcontract_info *cp, Eval* eval,const cpTokens = CCinit(&tokensC, T::EvalCode()); // non-fungible tokens support: - GetTokenData(assetid, tokenData, vopretNonfungible); - uint64_t royaltyFract = 0; // royalty is N in N/1000 fraction - if (vopretNonfungible.size() > 0) { - cpAssets->evalcodeNFT = vopretNonfungible.begin()[0]; - GetNftDataAsUint64(vopretNonfungible, NFTPROP_ROYALTY, royaltyFract); - if (royaltyFract > NFTROYALTY_DIVISOR-1) - royaltyFract = NFTROYALTY_DIVISOR-1; // royalty upper limit + GetTokenData(eval, assetid, tokenData, vextraData); + int64_t royaltyFract = 0; // royalty is N in N/1000 fraction + if (vextraData.size() > 0) { + GetTokelDataAsInt64(vextraData, TKLPROP_ROYALTY, royaltyFract); + if (royaltyFract > TKLROYALTY_DIVISOR-1) + royaltyFract = TKLROYALTY_DIVISOR-1; // royalty upper limit } - vuint8_t ownerpubkey = std::get<0>(tokenData); - Getscriptaddress(ownerNormalAddr, CScript() << ownerpubkey << OP_CHECKSIG); + vuint8_t vtokenCreatorPubkey = std::get<0>(tokenData); + Getscriptaddress(tokenCreatorNormalAddr, CScript() << vtokenCreatorPubkey << OP_CHECKSIG); // find dual-eval tokens global addr where tokens are locked: GetTokensCCaddress(cpAssets, tokensDualEvalUnspendableCCaddr, GetUnspendable(cpAssets, NULL), A::IsMixed()); // originator cc address, this is for marker validation: - GetCCaddress(cpAssets, origAssetsCCaddr, vorigpubkey, A::IsMixed()); + GetCCaddress(cpAssets, origAssetsCCaddr, pubkey2pk(vorigpubkey), A::IsMixed()); + + // global cc address: + GetCCaddress(cpAssets, globalAssetsCCaddr, GetUnspendable(cpAssets, NULL), A::IsMixed()); + + // marker cc address: + GetCCaddress1of2(cpAssets, markerCCaddress, pubkey2pk(vorigpubkey), GetUnspendable(cpAssets, NULL), A::IsMixed()); + + // cancelask/bid could have no normal vins (taking txfee from marker): + // if( IsCCInput(tx.vin[0].scriptSig) != false ) // vin0 should be normal vin + // return eval->Invalid("illegal asset cc vin0"); - if( IsCCInput(tx.vin[0].scriptSig) != false ) // vin0 should be normal vin - return eval->Invalid("illegal asset cc vin0"); - else if( numvouts < 2 ) + if(tx.vout.size() < 2) return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below if (assetid == zeroid) @@ -244,16 +256,20 @@ static bool AssetsValidateInternal(struct CCcontract_info *cp, Eval* eval,const char origTokenAddr[KOMODO_ADDRESS_BUFSIZE]; //preventCCvouts = 2; - if (numvouts < 3) + if (tx.vout.size() < 3) return eval->Invalid("too few vouts"); - else if( A::ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0LL, A::EvalCode()) == false ) + else if(A::ConstrainVout(tx.vout[0], CCVOUT, globalAssetsCCaddr, 0LL, A::EvalCode()) == false) return eval->Invalid("invalid funding for bid"); - else if( A::ConstrainVout(tx.vout[1], 1, origAssetsCCaddr, ASSETS_MARKER_AMOUNT, A::EvalCode()) == false ) // marker to originator asset cc addr + else if(A::ConstrainVout(tx.vout[1], CCVOUT, markerCCaddress, ASSETS_MARKER_AMOUNT, A::EvalCode()) == false) // marker to originator asset cc addr return eval->Invalid("invalid vout1 marker for original pubkey"); - else if( TotalPubkeyNormalInputs(tx, pubkey2pk(vorigpubkey)) == 0 ) // check tx is signed by originator pubkey - return eval->Invalid("not the originator pubkey signed"); - - // check should not be assets cc vins: + else if(TotalPubkeyNormalInputs(eval, tx, pubkey2pk(vorigpubkey)) == 0) // check tx is signed by originator pubkey + return eval->Invalid("not the originator pubkey signed for bid"); + else if (unit_price <= ASSETS_NORMAL_DUST) + return eval->Invalid("invalid or too low unit price"); + else if (tx.vout[0].nValue < unit_price) + return eval->Invalid("invalid bid amount too low"); + + // check it should not be assets cc vins for tokenv2bid at all: for (auto const &vin : tx.vin) { if (cpAssets->ismyvin(vin.scriptSig)) return eval->Invalid("could not have cc assets vin for creation tx"); @@ -272,114 +288,183 @@ static bool AssetsValidateInternal(struct CCcontract_info *cp, Eval* eval,const //vout.n-1: opreturn [EVAL_ASSETS] ['o'] ccvins = 2; // order and marker ccvouts = 0; - if (vin_assetoshis = AssetValidateBuyvin(cpAssets, eval, vin_unit_price, vin_origpubkey, origCCaddrDummy, origNormalAddr, tx, assetid) == 0) - return(false); + + vin_coins = AssetValidateBuyvin(cpAssets, eval, vin_unit_price, vin_origpubkey, origCCaddrDummy, origNormalAddr, vin_expiryHeight, tx, assetid); + if (vin_coins == 0) + return false; // eval already set if (tx.vout[0].nValue > ASSETS_NORMAL_DUST) { - if( A::ConstrainVout(tx.vout[0], 0, origNormalAddr, vin_assetoshis, 0) == false ) + if (!A::ConstrainVout(tx.vout[0], NORMALVOUT, origNormalAddr, vin_coins, 0)) return eval->Invalid("invalid refund for cancelbid"); } else { - bool isCCVout = A::ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, vin_assetoshis, 0); - if( A::ConstrainVout(tx.vout[0], 0, origNormalAddr, vin_assetoshis, 0) == false && - !isCCVout ) // dust allowed to go back - return eval->Invalid("invalid refund for cancelbid"); - if( isCCVout ) - ccvouts ++; + // dust must go to global cc address + if (!A::ConstrainVout(tx.vout[0], CCVOUT, globalAssetsCCaddr, vin_coins, 0)) + return eval->Invalid("invalid dust refund for cancelbid"); + ccvouts ++; + } + // get first ccvin: + ccvin = std::find_if(tx.vin.begin(), tx.vin.end(), [&](const CTxIn &vin) { return cpAssets->ismyvin(vin.scriptSig); }); + if (ccvin == tx.vin.end()) + return eval->Invalid("cc vin not found"), 0LL; + // get marker ccvin: + markervin = ccvin + 1; + if (markervin >= tx.vin.end()) + return eval->Invalid("marker vin not found"), 0LL; + + if (check_signing_pubkey(markervin->scriptSig) != pubkey2pk(vin_origpubkey)) { // check marker is signed by originator pubkey + if (eval->GetCurrentHeight() < vin_expiryHeight) + return eval->Invalid("not the originator pubkey signed for cancelbid for not yet expired orders"); + // allow to sign with globalpk to cancel the expired order } - if( TotalPubkeyNormalInputs(tx, pubkey2pk(vin_origpubkey)) == 0 ) // check tx is signed by originator pubkey - return eval->Invalid("not the originator pubkey signed"); - - //preventCCvins = 2; - //preventCCvouts = 0; - //fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origNormalAddr); break; case 'B': // fillbid: //vin.0: normal input //vin.1: cc assets 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 cc assets global address (if sufficient to buy a token or dust) //vout.1: vin.1 value to signer of vin.2 - //vout.2: vin.2 assetoshis to original pubkey (-royalty) + //vout.2: vin.2 satoshi to original pubkey (except royalty if any) //vout.3: royalty to token owner (optional) - //vout.4: CC output for assetoshis change (if any) + //vout.4: CC output for satoshi change (if any) //vout.5: normal output for change (if any) //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey] - //preventCCvouts = 4; ccvins = 1; - ccvouts = 3; + ccvouts = 0; + if (tx.vout.size() < 2) + return eval->Invalid("too few vouts"), 0LL; - if( (vin_assetoshis = AssetValidateBuyvin(cpAssets, eval, vin_unit_price, vin_origpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) - return false; - else if( numvouts < 4 ) + vin_coins = AssetValidateBuyvin(cpAssets, eval, vin_unit_price, vin_origpubkey, origTokensCCaddr, origNormalAddr, vin_expiryHeight, tx, assetid); + if (vin_coins == 0) + return false; // eval already set + else if(tx.vout.size() < 3) return eval->Invalid("not enough vouts for fillbid"); - else if( vin_origpubkey != vorigpubkey ) // originator pk does not change in the new opret + else if(vin_origpubkey != vorigpubkey) // originator pk does not change in the new opret return eval->Invalid("mismatched opreturn originator pubkeys for fillbid"); else if (unit_price != vin_unit_price) return eval->Invalid("mismatched unit price for fillbid"); + else if (unit_price <= 0) + return eval->Invalid("invalid unit price"); + else if (expiryHeight != vin_expiryHeight) { + return eval->Invalid("invalid expiry height"); + } else { - int32_t r = royaltyFract > 0 ? 1 : 0; - CAmount assetoshis = tx.vout[0].nValue + tx.vout[1].nValue + (royaltyFract > 0 ? tx.vout[2].nValue : 0); - if( vin_assetoshis != assetoshis ) // coins -> global cc address (remainder) + normal self address - return eval->Invalid("input cc value doesnt match vout0+1" + std::string(royaltyFract > 0 ? "+2" : "") + " for fillbid"); + if (royaltyFract < 0LL || royaltyFract >= TKLROYALTY_DIVISOR) + return eval->Invalid("invalid royalty value"); + + // moved under CCUpgrades condition: + // CAmount royaltyValue = royaltyFract > 0 ? vin_coins / TKLROYALTY_DIVISOR * royaltyFract : 0; + //if (royaltyValue <= ASSETS_NORMAL_DUST) + // royaltyValue = 0LL; // reset if dust + + int32_t r = royaltyFract > 0LL ? 1 : 0; + bool isRoyaltyDust = true; // if no royalty then to order creator + + // check if royalty or paid_value is not dust + if (royaltyFract > 0) + { + if (CCUpgrades::IsUpgradeActive(eval->GetCurrentTime(), eval->GetCurrentHeight(), CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1) == false) { + // old bad calc: + // incorrect use of vin_coins to calculate royalty (this is true only for nfts) + CAmount royaltyValue = royaltyFract > 0 ? vin_coins / TKLROYALTY_DIVISOR * royaltyFract : 0; + // std::cerr << __func__ << " fillbid=" << royaltyValue << " dust=" << ASSETS_NORMAL_DUST << std::endl; + if (royaltyValue <= ASSETS_NORMAL_DUST) { + isRoyaltyDust = true; // always to order creator + r = 0; // no royalty vout + } + } + else { + // upgrade June 2022 HF + int32_t tokenVout = 1; + // find first cc vout (token to buyer): + for(; tokenVout < tx.vout.size() && !tx.vout[tokenVout].scriptPubKey.IsPayToCryptoCondition(); tokenVout ++) {} + if (tokenVout == tx.vout.size()) return eval->Invalid("could not find token vout for fillbid"); + // fixed dust calc: + if (AssetsFillOrderIsDust(royaltyFract, tx.vout[tokenVout].nValue * vin_unit_price, isRoyaltyDust)) + r = 0; // no dedicated royalty vout, total goes to tx.vout[2].nValue + } + } + + CAmount assetoshis = tx.vout[0].nValue + tx.vout[1].nValue + (r ? tx.vout[2].nValue : 0LL); + if( vin_coins != assetoshis ) // coins -> global cc address (remainder) + normal self address + return eval->Invalid("input cc value does not equal to vout0+1" + std::string(r ? "+2" : "") + " for fillbid"); + + // coins remainder + if (tx.vout[0].nValue / unit_price > 0 || tx.vout[0].nValue <= ASSETS_NORMAL_DUST) // there are remaining tokens to buy or dust must return to global cc address + { + if (!A::ConstrainVout(tx.vout[0], CCVOUT, globalAssetsCCaddr, 0LL, A::EvalCode())) // if remainder sufficient to buy tokens -> coins to asset global cc addr + return eval->Invalid("mismatched vout0 global assets CC addr for fillbid"); + ccvouts ++; + } + else + { // if remaining_units == 0 (empty bid) then the remainder should go to the order originator normal address, if it is not dust + if (!A::ConstrainVout(tx.vout[0], NORMALVOUT, origNormalAddr, 0LL, 0)) // remainder less than token price should return to originator normal addr + return eval->Invalid("vout0 should be order originator normal address with remainder for fillbid"); + } + + vin_tokens = AssetsGetTxTokenInputs(eval, cpTokens, tx); + int32_t myNormalVout = 1; + int32_t myTokenVout = 2+r; + int32_t tokenRemainderVout; + if (tx.vout[0].nValue / unit_price > 0) + tokenRemainderVout = 4+r; // marker present before this vout + else + tokenRemainderVout = 3+r; // no marker - vin_tokens = AssetsGetCCInputs(cpTokens, NULL, tx); - if( tx.vout[4+r].scriptPubKey.IsPayToCryptoCondition() != false ) // if tokens remainder exists + if (!AssetsValidateTokenId_Activated(eval, cp, tx, myTokenVout, assetid)) + return eval->Invalid("invalid tokenid in vout for tokenfillbid"); + if (tx.vout.size() > tokenRemainderVout && tx.vout[tokenRemainderVout].scriptPubKey.IsPayToCryptoCondition()) // if tokens remainder exists { - if( A::ConstrainVout(tx.vout[2+r], 1, origTokensCCaddr, 0LL, T::EvalCode()) == false ) // tokens to originator cc addr (tokens+nonfungible evalcode) - return eval->Invalid("vout" + std::to_string(2+r) + " tokens value should go to originator pubkey for fillbid"); - else if( vin_tokens != tx.vout[2+r].nValue + tx.vout[4+r].nValue ) // tokens from cc global address -> token global addr (remainder) + originator cc address - return eval->Invalid("tokens inputs doesnt match tokens vout" + std::to_string(2+r) + "+" + "vout"+std::to_string(4+r) + " for fillbid"); - //preventCCvouts ++; - ccvouts++; + if (!A::ConstrainVout(tx.vout[myTokenVout], CCVOUT, origTokensCCaddr, 0LL, T::EvalCode())) // tokens to originator cc addr (tokens+nonfungible evalcode) + return eval->Invalid("vout" + std::to_string(myTokenVout) + " tokens value should go to originator pubkey for fillbid"); + else if (vin_tokens != tx.vout[myTokenVout].nValue + tx.vout[tokenRemainderVout].nValue) // tokens from cc inputs -> bid originator token pk + token remainder anywhere + return eval->Invalid("tokens inputs doesnt match tokens vout" + std::to_string(myTokenVout) + "+" + "vout"+std::to_string(tokenRemainderVout) + " for fillbid"); + ccvouts += 2; + } + else { + if (tx.vout.size() <= myTokenVout || !A::ConstrainVout(tx.vout[myTokenVout], CCVOUT, origTokensCCaddr, vin_tokens, T::EvalCode())) // all tokens to originator cc addr, no cc change present + return eval->Invalid("vout" + std::to_string(myTokenVout) + " should have tokens to originator cc addr for fillbid"); + ccvouts ++; } - else if( A::ConstrainVout(tx.vout[2+r], 1, origTokensCCaddr, vin_tokens, T::EvalCode()) == false ) // all tokens to originator cc addr, no cc change present - return eval->Invalid("vout" + std::to_string(2+r) + " should have tokens to originator cc addr for fillbid"); + if (!A::ConstrainVout(tx.vout[myNormalVout], NORMALVOUT, NULL, 0LL, 0)) // amount paid for tokens goes to normal addr (could be any address selected by token seller) + return eval->Invalid("vout " + std::to_string(myNormalVout) + " should be normal for fillbid"); - if( A::ConstrainVout(tx.vout[1], 0, NULL, 0LL, 0) == false ) // amount paid for tokens goes to normal addr (we can't check 'self' address) - return eval->Invalid("vout1 should be normal for fillbid"); - else if( A::ConstrainVout(tx.vout[3+r], 1, origAssetsCCaddr, ASSETS_MARKER_AMOUNT, A::EvalCode()) == false ) // marker to originator asset cc addr - return eval->Invalid("invalid vout" + std::to_string(3+r) + " marker for originator pubkey for fillbid"); + if (tx.vout[0].nValue / unit_price > 0) { // bid coins remainder sufficient for more tokens - marker should exist + int32_t markerVout = 3 + r; + if (tx.vout.size() <= markerVout || !A::ConstrainVout(tx.vout[markerVout], CCVOUT, markerCCaddress, ASSETS_MARKER_AMOUNT, A::EvalCode())) // marker to originator asset cc addr + return eval->Invalid("invalid vout" + std::to_string(markerVout) + " marker for originator pubkey for fillbid"); + ccvouts ++; + } - CAmount received_value = royaltyFract > 0 ? tx.vout[1].nValue + tx.vout[2].nValue : tx.vout[1].nValue; // vout1 paid value to seller, vout2 royalty to owner + CAmount received_value = r ? tx.vout[1].nValue + tx.vout[2].nValue : tx.vout[1].nValue; // vout1 paid value to seller, vout2 royalty to owner CAmount paid_units = tx.vout[2+r].nValue; - if( ValidateBidRemainder(unit_price, tx.vout[0].nValue, vin_assetoshis, received_value, paid_units) == false ) // check real price and coins spending from global addr - return eval->Invalid("vout" + std::to_string(2+r) + " mismatched remainder for fillbid"); - if (unit_price <= 0) - return eval->Invalid("invalid unit price"); - if (royaltyFract > 0) { - if ((tx.vout[1].nValue + tx.vout[2].nValue) / NFTROYALTY_DIVISOR * royaltyFract != tx.vout[2].nValue) // validate royalty value + if (!ValidateBidRemainder(unit_price, tx.vout[0].nValue, vin_coins, received_value, paid_units)) // check real price and coins spending from global addr + return eval->Invalid("vout" + std::to_string(0) + " mismatched remainder for fillbid"); + if (r) { + if ((tx.vout[1].nValue + tx.vout[2].nValue) / TKLROYALTY_DIVISOR * royaltyFract != tx.vout[2].nValue) // validate royalty value return eval->Invalid("vout2 invalid royalty amount for fillask"); - if( A::ConstrainVout(tx.vout[2], 0, ownerNormalAddr, 0LL, 0) == false ) // validate owner royalty dest + if (!A::ConstrainVout(tx.vout[2], NORMALVOUT, tokenCreatorNormalAddr, 0LL, 0)) // validate owner royalty dest return eval->Invalid("vout2 invalid royalty detination for fillask"); } - if( tx.vout[0].nValue / unit_price != 0 ) // remaining tokens to buy - { - if( A::ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0LL, A::EvalCode()) == false ) // if remainder sufficient to buy tokens -> coins to asset global cc addr - return eval->Invalid("mismatched vout0 global assets CC addr for fillbid"); - } - else - { // remaining_units == 0 - if( tx.vout[0].nValue > ASSETS_NORMAL_DUST ) { - if( A::ConstrainVout(tx.vout[0], 0, origNormalAddr, 0LL, 0) == false ) // remainder less than token price should return to originator normal addr - return eval->Invalid("vout0 should be originator normal address with remainder for fillbid"); - ccvouts--; - } - else { - bool isCCVout = A::ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0, 0); - if( A::ConstrainVout(tx.vout[0], 0, origNormalAddr, 0LL, 0) == false && - !isCCVout ) // dust allowed to go back to cc global addr - return eval->Invalid("vout0 should be originator normal address with remainder or dust should go back to global addr for fillbid"); - if( !isCCVout ) - ccvouts--; + else { + if (CCUpgrades::IsUpgradeActive(eval->GetCurrentTime(), eval->GetCurrentHeight(), CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)) + { + // validate that the paid value to go to the token owner if amount to seller is dust + if (royaltyFract > 0 && r == 0 && !isRoyaltyDust) { // there is royalty dust and royalty is not dust --> send to token owner + if (!A::ConstrainVout(tx.vout[myNormalVout], NORMALVOUT, tokenCreatorNormalAddr, 0LL, 0)) // amount paid for tokens goes to normal addr (we can't check 'self' address) + return eval->Invalid("vout " + std::to_string(myNormalVout) + " should go to token owner for fillbid (dust reason)"); + } } } + if (eval->GetCurrentHeight() >= vin_expiryHeight) + return eval->Invalid("order is expired"); } - //fprintf(stderr,"fillbuy validated\n"); break; + //case 'e': // sell swap offer // break; // disable swaps + case 's': // ask offer //vin.0: normal input //vin.1+: valid CC output for sale @@ -397,24 +482,25 @@ static bool AssetsValidateInternal(struct CCcontract_info *cp, Eval* eval,const // for EVAL_ASSETSV2 check the creation tx char origTokenAddr[KOMODO_ADDRESS_BUFSIZE]; CPubKey origpk = pubkey2pk(vorigpubkey); - - //preventCCvouts = 2; ccvouts = 2; - if (numvouts < 3) + if (tx.vout.size() < 3) return eval->Invalid("too few vouts"); - else if (A::ConstrainVout(tx.vout[0], 1, tokensDualEvalUnspendableCCaddr, 0LL, T::EvalCode()) == false) // tokens sent to global addr + else if (A::ConstrainVout(tx.vout[0], CCVOUT, tokensDualEvalUnspendableCCaddr, 0LL, T::EvalCode()) == false) // tokens sent to global addr return eval->Invalid("invalid vout0 global two eval address for sell"); - else if( A::ConstrainVout(tx.vout[1], 1, origAssetsCCaddr, ASSETS_MARKER_AMOUNT, A::EvalCode()) == false ) // marker to originator asset cc addr + else if (!AssetsValidateTokenId_Activated(eval, cp, tx, 0, assetid)) + return eval->Invalid("invalid tokenid in output for tokenask"); + else if( A::ConstrainVout(tx.vout[1], CCVOUT, markerCCaddress, ASSETS_MARKER_AMOUNT, A::EvalCode()) == false ) // marker to originator asset cc addr return eval->Invalid("invalid vout1 marker for originator pubkey"); - else if (TotalPubkeyNormalInputs(tx, origpk) == 0) // check tx is signed by originator pubkey - return eval->Invalid("not the originator pubkey signed"); + else if (TotalPubkeyNormalInputs(eval, tx, origpk) == 0) // check tx is signed by originator pubkey + return eval->Invalid("not the originator pubkey signed for ask"); + else if (unit_price <= ASSETS_NORMAL_DUST) + return eval->Invalid("invalid or too low unit price"); - //GetTokensCCaddress(cpTokens, origTokenAddr, origpk, A::IsMixed()); if (tx.vout[2].scriptPubKey.IsPayToCryptoCondition()) if (!tx.vout[2].scriptPubKey.IsPayToCCV2() || tx.vout[2].scriptPubKey.SpkHasEvalcodeCCV2(T::EvalCode())) // have token change - ccvouts ++; // preventCCvouts ++; + ccvouts ++; - // check should not be assets cc vins: + // check should not be assets cc vins for tokenv2ask at all: for (auto const &vin : tx.vin) { if (cpAssets->ismyvin(vin.scriptSig)) return eval->Invalid("could not have cc assets vin for creation tx"); @@ -430,17 +516,34 @@ static bool AssetsValidateInternal(struct CCcontract_info *cp, Eval* eval,const //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 (tx.vout.size() < 2) + return eval->Invalid("too few vouts"), 0LL; - if( (vin_tokens = AssetValidateSellvin(cpAssets, eval, vin_unit_price, vin_origpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) + vin_tokens = AssetValidateSellvin(cpAssets, eval, vin_unit_price, vin_origpubkey, origTokensCCaddr, origNormalAddr, vin_expiryHeight, tx, assetid); + if (vin_tokens == 0) return false; // eval already set - else if (A::ConstrainVout(tx.vout[0], 1, origTokensCCaddr, vin_tokens, T::EvalCode()) == false) // tokens returning to originator cc addr + + if (A::ConstrainVout(tx.vout[0], CCVOUT, origTokensCCaddr, vin_tokens, T::EvalCode()) == false) // tokens returning to originator cc addr return eval->Invalid("invalid vout0 for cancelask"); - else if (TotalPubkeyNormalInputs(tx, pubkey2pk(vin_origpubkey)) == 0) // check tx is signed by originator pubkey - return eval->Invalid("not the originator pubkey signed"); - //preventCCvins = 3; - //preventCCvouts = 1; - ccvins = 2; // order and marker - ccvouts = 1; + else if (!AssetsValidateTokenId_Activated(eval, cp, tx, 0, assetid)) + return eval->Invalid("invalid tokenid in vout0 for cancelask"); + + // get first ccvin: + ccvin = std::find_if(tx.vin.begin(), tx.vin.end(), [&](const CTxIn &vin) { return cpAssets->ismyvin(vin.scriptSig); }); + if (ccvin == tx.vin.end()) + return eval->Invalid("cc vin not found"), 0LL; + // get marker ccvin: + markervin = ccvin + 1; + if (markervin >= tx.vin.end()) + return eval->Invalid("marker vin not found"), 0LL; + + if (check_signing_pubkey(markervin->scriptSig) != pubkey2pk(vin_origpubkey)) { // check marker is signed by originator pubkey + if (eval->GetCurrentHeight() < vin_expiryHeight) + return eval->Invalid("not the originator pubkey signed for cancelask for not yet expired orders"); + // allow to sign with globalpk to cancel the expired order + } + ccvins = 2; // order and marker vins + ccvouts = 1; // token back to the originator break; case 'S': // fill ask @@ -453,46 +556,89 @@ static bool AssetsValidateInternal(struct CCcontract_info *cp, Eval* eval,const //vout.3: normal output for change (if any) //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey] ccvins = 1; - ccvouts = 3; - - if( (vin_tokens = AssetValidateSellvin(cpAssets, eval, vin_unit_price, vin_origpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) - return false; // eval is set - else if( numvouts < 4 ) + ccvouts = 2; + vin_tokens = AssetValidateSellvin(cpAssets, eval, vin_unit_price, vin_origpubkey, origTokensCCaddr, origNormalAddr, vin_expiryHeight, tx, assetid); + if (vin_tokens == 0) + return false; // eval is already set + else if (tx.vout.size() < 3) return eval->Invalid("not enough vouts for fillask"); - else if( vin_origpubkey != vorigpubkey ) + else if (vin_origpubkey != vorigpubkey) return eval->Invalid("mismatched origpubkeys for fillask"); else if (unit_price != vin_unit_price) return eval->Invalid("mismatched unit price for fillask"); + else if (expiryHeight != vin_expiryHeight) { + return eval->Invalid("invalid expiry height"); + } else { - int32_t markerVout = royaltyFract > 0 ? 4 : 3; - if( vin_tokens != tx.vout[0].nValue + tx.vout[1].nValue ) + if (vin_tokens != tx.vout[0].nValue + tx.vout[1].nValue) return eval->Invalid("locked value doesnt match vout0+1 fillask"); + if (royaltyFract < 0LL || royaltyFract >= TKLROYALTY_DIVISOR) + return eval->Invalid("invalid royalty value"); + + int32_t r = royaltyFract > 0 ? 1 : 0; + bool isRoyaltyDust = true; // if no royalty then to order creator + // check if royalty or paid_value is not dust + if (royaltyFract > 0) + { + if (CCUpgrades::IsUpgradeActive(eval->GetCurrentTime(), eval->GetCurrentHeight(), CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1) == false) { + // old bad calc: only if royalty<=50% works bcz of loss of significance bcz of division by royaltyFract first. + // also wrong assumption that paid_value is subtracted by the royalty (in fact it is sum of both if the royalty is dust) + // suppose the nValue is such that the royalty_value is assets' dust: + // std::cerr << __func__ << " fillask tx.vout[2].nValue=" << tx.vout[2].nValue << " dust=" << ASSETS_NORMAL_DUST / royaltyFract * TKLROYALTY_DIVISOR - ASSETS_NORMAL_DUST << std::endl; + if (tx.vout[2].nValue <= ASSETS_NORMAL_DUST / royaltyFract * TKLROYALTY_DIVISOR - ASSETS_NORMAL_DUST) { // if value paid to seller less than when the royalty is minimum + isRoyaltyDust = true; // always to order creator + r = 0; // no royalty vout bcz of dust + //std::cerr << __func__ << " old calc, tx.vout[2].nValue=" << tx.vout[2].nValue << " test dust=" << ASSETS_NORMAL_DUST / royaltyFract * TKLROYALTY_DIVISOR - ASSETS_NORMAL_DUST << std::endl; + } + } + else { + // fixed dust calc: + if (AssetsFillOrderIsDust(royaltyFract, vin_tokens * vin_unit_price, isRoyaltyDust)) { + r = 0; // no dedicated royalty vout bcz of dust, total goes to tx.vout[2].nValue + } + } + } - CAmount paid_value = royaltyFract > 0 ? tx.vout[2].nValue+tx.vout[3].nValue : tx.vout[2].nValue; // vout2 paid value to seller, vout3 royalty to owner - if( ValidateAskRemainder(unit_price, tx.vout[0].nValue, vin_tokens, tx.vout[1].nValue, paid_value) == false ) + CAmount paid_value = r > 0 ? tx.vout[2].nValue + tx.vout[3].nValue : tx.vout[2].nValue; // vout2 paid value to seller, vout3 royalty to owner + if (!ValidateAskRemainder(unit_price, tx.vout[0].nValue, vin_tokens, tx.vout[1].nValue, paid_value)) return eval->Invalid("mismatched vout0 remainder for fillask"); - else if( A::ConstrainVout(tx.vout[1], 1, NULL, 0LL, T::EvalCode()) == false ) // do not check tokens buyer's 'self' cc addr + else if (!AssetsValidateTokenId_Activated(eval, cp, tx, 0, assetid)) + return eval->Invalid("invalid tokenid in vout0 for fillask"); + else if (!A::ConstrainVout(tx.vout[1], CCVOUT, NULL, 0LL, T::EvalCode())) // do not check tokens buyer's 'self' cc addr return eval->Invalid("vout1 should be cc for fillask"); - else if( A::ConstrainVout(tx.vout[2], 0, origNormalAddr, 0LL, 0) == false ) // coins to originator normal addr - return eval->Invalid("vout2 should be cc for fillask"); - if (royaltyFract > 0) { - if ((tx.vout[2].nValue + tx.vout[3].nValue) / NFTROYALTY_DIVISOR * royaltyFract != tx.vout[3].nValue) // validate royalty value + else if (!AssetsValidateTokenId_Activated(eval, cp, tx, 1, assetid)) + return eval->Invalid("invalid tokenid in vout1 for fillask"); + else { + if (CCUpgrades::IsUpgradeActive(eval->GetCurrentTime(), eval->GetCurrentHeight(), CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1) == false) { + if (!A::ConstrainVout(tx.vout[2], NORMALVOUT, origNormalAddr, 0LL, 0)) // coins to originator normal addr + return eval->Invalid("vout2 should be cc for fillask"); + } + else { + if (!A::ConstrainVout(tx.vout[2], NORMALVOUT, (royaltyFract > 0 && r == 0 && !isRoyaltyDust ? tokenCreatorNormalAddr : origNormalAddr), 0LL, 0)) // coins to order originator normal addr (if royalty dust then to token creator) + return eval->Invalid(std::string("vout2 should be normal to ") + (royaltyFract > 0 && r == 0 && !isRoyaltyDust ? std::string("token creator") : std::string("order creator")) + " for fillask"); + } + } + if (r > 0) { + if ((tx.vout[2].nValue + tx.vout[3].nValue) / TKLROYALTY_DIVISOR * royaltyFract != tx.vout[3].nValue) // validate royalty value return eval->Invalid("vout3 invalid royalty amount for fillask"); - if( A::ConstrainVout(tx.vout[3], 0, ownerNormalAddr, 0LL, 0) == false ) // validate owner royalty dest - return eval->Invalid("vout3 invalid royalty detination for fillask"); + if (!A::ConstrainVout(tx.vout[3], NORMALVOUT, tokenCreatorNormalAddr, 0LL, 0)) // validate owner royalty dest + return eval->Invalid("vout3 invalid royalty destination for fillask"); } - if( A::ConstrainVout(tx.vout[markerVout], 1, origAssetsCCaddr, ASSETS_MARKER_AMOUNT, A::EvalCode()) == false ) // marker to originator asset cc addr - return eval->Invalid("invalid marker vout for original pubkey"); - else // if( remaining_units != 0 ) // remaining expected amount in coins, should be anyway - { - if( A::ConstrainVout(tx.vout[0], 1, tokensDualEvalUnspendableCCaddr, 0LL, A::EvalCode()) == false ) // tokens remainder on global addr - return eval->Invalid("mismatched vout0 two eval global CC addr for fillask"); + if (!A::ConstrainVout(tx.vout[0], CCVOUT, tokensDualEvalUnspendableCCaddr, 0LL, A::EvalCode())) // tokens remainder on global addr + return eval->Invalid("invalid vout0 should pay to tokens/assets global address for fillask"); + if (tx.vout[0].nValue > 0) { + int32_t markerVout = r > 0 ? 4 : 3; + // marker should exist if remainder not empty + if (tx.vout.size() <= markerVout || A::ConstrainVout(tx.vout[markerVout], CCVOUT, markerCCaddress, ASSETS_MARKER_AMOUNT, A::EvalCode()) == false) // marker to originator asset cc addr + return eval->Invalid("invalid marker vout for original pubkey"); + ccvouts ++; } + if (eval->GetCurrentHeight() >= vin_expiryHeight) + return eval->Invalid("order is expired"); } - - //fprintf(stderr,"fill validated\n"); break; + case 'E': // fillexchange ////////// not implemented yet //////////// return eval->Invalid("unexpected assets fillexchange funcid"); @@ -511,23 +657,22 @@ static bool AssetsValidateInternal(struct CCcontract_info *cp, Eval* eval,const // eval->Invalid("asset2 inputs != outputs"); ////////// not implemented yet //////////// - if( (vin_tokens = AssetValidateSellvin(cpTokens, eval, vin_unit_price, vin_origpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) + /*if( (vin_tokens = AssetValidateSellvin(cpTokens, eval, vin_unit_price, vin_origpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) return(false); - else if( numvouts < 3 ) + else if(tx.vout.size() < 3) return eval->Invalid("not enough vouts for fillex"); - else if( vin_origpubkey != vorigpubkey ) + else if(vin_origpubkey != vorigpubkey) 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 ) return eval->Invalid("locked value doesnt match vout0+1 fillex"); else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != false ) ////////// not implemented yet //////////// { - if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == false ) + if( ConstrainVout(tx.vout[2], CCVOUT, origTokensCCaddr, 0) == false ) return eval->Invalid("vout2 doesnt go to origpubkey fillex"); else if( tokens != tx.vout[2].nValue + tx.vout[3].nValue ) { @@ -536,36 +681,30 @@ static bool AssetsValidateInternal(struct CCcontract_info *cp, Eval* eval,const } } ////////// not implemented yet //////////// - else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, tokens) == false ) + else if( ConstrainVout(tx.vout[2], CCVOUT, origTokensCCaddr, tokens) == false ) return eval->Invalid("vout2 doesnt match inputs fillex"); - else if( ConstrainVout(tx.vout[1], 0, 0, 0) == false ) + else if( ConstrainVout(tx.vout[1], NORMALVOUT, 0, 0) == false ) 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 ) + else if( ConstrainVout(tx.vout[1], CCVOUT, 0, 0) == false ) ////////// not implemented yet //////////// return eval->Invalid("normal vout1 for fillex"); else if( remaining_units != 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], CCVOUT, (char *)cpAssets->unspendableCCaddr, 0) == false ) // 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; } - //bool bPrevent = PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts); // prevent presence of unknown cc vin or cc vouts in the tx - //std::cerr << "AssetsValidate() PreventCC returned=" << bPrevent << std::endl; - //return (bPrevent); // replaced PreventCC with min/max cc vin/vout calc to allow vin position flexibility: if (!CountCCVinVouts(cpAssets, tx, ccvins, ccvouts)) return eval->Invalid("invalid cc vin or vout count"); @@ -575,20 +714,6 @@ static bool AssetsValidateInternal(struct CCcontract_info *cp, Eval* eval,const // redirect to AssetsValidateInternal and log error bool AssetsValidate(struct CCcontract_info *cpAssets, Eval* eval,const CTransaction &tx, uint32_t nIn) { - // 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 (!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; diff --git a/src/cc/auction.cpp b/src/cc/auction.cpp index 88d5134af53..3aa49082867 100644 --- a/src/cc/auction.cpp +++ b/src/cc/auction.cpp @@ -164,7 +164,7 @@ std::string AuctionBid(uint64_t txfee,uint256 itemhash,int64_t amount) if ( CCchange != 0 ) mtx.vout.push_back(MakeCC1vout(EVAL_AUCTION,CCchange,Auctionpk)); mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,opret)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); } else fprintf(stderr,"cant find Auction inputs\n"); return(""); } @@ -185,7 +185,7 @@ std::string AuctionDeliver(uint64_t txfee,uint256 itemhash,uint256 bidtxid) if ( CCchange != 0 ) mtx.vout.push_back(MakeCC1vout(EVAL_AUCTION,CCchange,Auctionpk)); mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,opret)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); } else fprintf(stderr,"cant find Auction inputs\n"); return(""); } diff --git a/src/cc/betprotocol.cpp b/src/cc/betprotocol.cpp index 0724f2fea7c..bc2875adfcb 100644 --- a/src/cc/betprotocol.cpp +++ b/src/cc/betprotocol.cpp @@ -264,7 +264,7 @@ bool Eval::DisputePayout(AppVM &vm, std::vector params, const CTransact if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock)) return Error("couldnt-get-parent"); - if (GetCurrentHeight() < sessionBlock.GetHeight() + waitBlocks) + if (GetCurrentHeightCompat() < sessionBlock.GetHeight() + waitBlocks) return Invalid("dispute-too-soon"); // Not yet } diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 36495314887..39faa2ba664 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -426,7 +426,7 @@ bool CClibExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx,unsigned int nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; - std::vector > txids; + std::vector > addressIndexOutputs; if ( cp->evalcode != EVAL_FAUCET2 ) { #ifdef BUILD_ROGUE @@ -482,8 +482,8 @@ bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C else if ( (hash[0] & 0xff) != 0 || (hash[31] & 0xff) != 0 ) return eval->Invalid("invalid faucetget txid"); Getscriptaddress(destaddr,tx.vout[i].scriptPubKey); - SetCCtxids(txids,destaddr,tx.vout[i].scriptPubKey.IsPayToCryptoCondition()); - for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) + SetAddressIndexOutputs(addressIndexOutputs,destaddr,tx.vout[i].scriptPubKey.IsPayToCryptoCondition()); + for (std::vector >::const_iterator it=addressIndexOutputs.begin(); it!=addressIndexOutputs.end(); it++) { //int height = it->first.blockHeight; if ( CCduration(numblocks,it->first.txhash) > 0 && numblocks > 3 ) @@ -608,7 +608,7 @@ std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *para for (i=0; i<1000000; i++,j++) { tmpmtx = mtx; - rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_FAUCET2 << (uint8_t)'G' << j)); + rawhex = FinalizeCCTx(0,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_FAUCET2 << (uint8_t)'G' << j)); if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) { len >>= 1; diff --git a/src/cc/disputepayout.cpp b/src/cc/disputepayout.cpp index 60924b00bae..1d0f8f1f8d9 100644 --- a/src/cc/disputepayout.cpp +++ b/src/cc/disputepayout.cpp @@ -61,7 +61,7 @@ bool Eval::DisputePayout(AppVM &vm, std::vector params, const CTransact if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock)) return Error("couldnt-get-parent"); - if (GetCurrentHeight() < sessionBlock.GetHeight() + waitBlocks) + if (GetCurrentHeightCompat() < sessionBlock.GetHeight() + waitBlocks) return Invalid("dispute-too-soon"); // Not yet } diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 3abb6f61da3..e0e78cf130a 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -34,9 +34,11 @@ Eval* EVAL_TEST = 0; struct CCcontract_info CCinfos[0x100]; extern pthread_mutex_t KOMODO_CC_mutex; -bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn,std::shared_ptr evalcodeChecker) +bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn, int64_t nTime, int32_t nHeight, std::shared_ptr evalcodeChecker) { EvalRef eval; + eval->SetCurrentTime(nTime); + eval->SetCurrentHeight(nHeight); pthread_mutex_lock(&KOMODO_CC_mutex); bool out = eval->Dispatch(cond, tx, nIn, evalcodeChecker); pthread_mutex_unlock(&KOMODO_CC_mutex); @@ -46,13 +48,17 @@ bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn,std::sha if (eval->state.IsValid()) return true; + if (evalcodeChecker != nullptr) + evalcodeChecker->lastEvalErrorState = eval->state; + + // report cc error: std::string lvl = eval->state.IsInvalid() ? "Invalid" : "Error!"; - fprintf(stderr, "CC Eval %s %s: %s spending tx %s\n", - EvalToStr(cond->code[0]).data(), - lvl.data(), - eval->state.GetRejectReason().data(), - tx.vin[nIn].prevout.hash.GetHex().data()); - if (eval->state.IsError()) fprintf(stderr, "Culprit: %s\n", EncodeHexTx(tx).data()); + LOGSTREAMFN("cc", CCLOG_ERROR, stream << "CC Eval evalcode: " << EvalToStr(cond->code[0]) << " " << lvl << ", reason: " << eval->state.GetRejectReason() << std::endl); + if (eval->state.IsError()) { + LOGSTREAMFN("cc", CCLOG_ERROR, stream << "CC Eval Culprit tx: " << EncodeHexTx(tx) << std::endl); + } + + /* this hangs komodod bcs of lock! CTransaction tmp; if (mempool.lookup(tx.GetHash(), tmp)) { @@ -60,8 +66,9 @@ bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn,std::sha // Miner will mine 1 invalid block, but doesnt stop them mining until a restart. // This would almost never happen in normal use. std::list dummy; - mempool.remove(tx,dummy,true); + //mempool.remove(tx,dummy,true); } + */ return false; } @@ -101,6 +108,9 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn,s cp->didinit = 1; } + if (GetCurrentHeight() <= 0) + return Invalid("current chain height not set for eval object"); + switch ( ecode ) { case EVAL_IMPORTPAYOUT: @@ -145,7 +155,16 @@ bool Eval::GetTxConfirmed(const uint256 &hash, CTransaction &txOut, CBlockIndex return true; } +int64_t Eval::GetCurrentTime() const +{ + return nCurrentTime; +} + unsigned int Eval::GetCurrentHeight() const +{ + return nCurrentHeight; +} +unsigned int Eval::GetCurrentHeightCompat() const { return chainActive.Height(); } @@ -259,3 +278,8 @@ uint256 GetMerkleRoot(const std::vector& vLeaves) std::vector vMerkleTree; return BuildMerkleTree(&fMutated, vLeaves, vMerkleTree); } + +bool GetTxUnconfirmedOpt(Eval *eval, const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) +{ + return eval ? eval->GetTxUnconfirmed(hash, txOut, hashBlock) : myGetTransaction(hash, txOut, hashBlock); +} \ No newline at end of file diff --git a/src/cc/eval.h b/src/cc/eval.h index c36a2e77d6a..7165b6e727f 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -16,6 +16,12 @@ #ifndef CC_EVAL_H #define CC_EVAL_H +#ifdef _WIN32 +#undef __cpuid +#endif +#include +#include + #include #include "cc/utils.h" @@ -61,7 +67,7 @@ EVAL(EVAL_KOGS, 0xf4) \ EVAL(EVAL_TOKENSV2, 0xf5) \ EVAL(EVAL_ASSETSV2, 0xf6) \ - EVAL(EVAL_NFTDATA, 0xf7) \ + EVAL(EVAL_TOKELDATA, 0xf7) \ // evalcodes 0x10 to 0x7f are reserved for cclib dynamic CC @@ -79,6 +85,7 @@ class CCheckCCEvalCodes; class Eval { public: + Eval() : nCurrentTime(0LL), nCurrentHeight(0) {} CValidationState state; bool Invalid(std::string s) { return state.Invalid(false, 0, s); } @@ -110,7 +117,11 @@ class Eval */ virtual bool GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const; virtual bool GetTxConfirmed(const uint256 &hash, CTransaction &txOut, CBlockIndex &block) const; + virtual int64_t GetCurrentTime() const; + void SetCurrentTime(int64_t nTimeIn) { nCurrentTime = nTimeIn; } virtual unsigned int GetCurrentHeight() const; + virtual unsigned int GetCurrentHeightCompat() const; + void SetCurrentHeight(int32_t nHeightIn) { nCurrentHeight = nHeightIn; } virtual bool GetSpendsConfirmed(uint256 hash, std::vector &spends) const; virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const; virtual int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const; @@ -118,6 +129,10 @@ class Eval virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const; virtual uint32_t GetAssetchainsCC() const; virtual std::string GetAssetchainsSymbol() const; + +private: + int64_t nCurrentTime; + int32_t nCurrentHeight; }; @@ -138,7 +153,7 @@ class EvalRef : public EvalRef_ -bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn, std::shared_ptr evalcodeChecker); +bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn, int64_t nTime, int32_t nHeight, std::shared_ptr evalcodeChecker); /* @@ -290,10 +305,45 @@ class MerkleBranch typedef std::pair TxProof; +// collect already validated evalcodes in a tx being validated +// to prevent repeated validation of the same evalcode +class CCheckCCEvalCodes +{ + //! The set of evalcodes that are already processed in CC validation. + std::map> evalcodes; + + //! Mutex to protect evalcodes map + boost::mutex mutex_eval; + +public: + void MarkEvalCode(uint256 txid, uint8_t ecode) + { + boost::unique_lock lock(mutex_eval); + auto search = evalcodes.find(txid); + if (search == evalcodes.end()) { + std::set tmp; + tmp.insert(ecode); + evalcodes[txid] = tmp; + } else + search->second.insert(ecode); + } + + bool CheckEvalCode(uint256 txid, uint8_t ecode) + { + boost::unique_lock lock(mutex_eval); + auto search = evalcodes.find(txid); + return search == evalcodes.end() ? false : (search->second.find(ecode) != search->second.end()); + } + CValidationState lastEvalErrorState; // store last eval error aborting the validation process +}; + uint256 GetMerkleRoot(const std::vector& vLeaves); struct CCcontract_info *CCinit(struct CCcontract_info *cp,uint8_t evalcode); bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector paramsNull, const CTransaction &tx, unsigned int nIn, std::shared_ptr evalcodeChecker); +// switches between get tx inside validation or ouside +bool GetTxUnconfirmedOpt(Eval *eval, const uint256 &hash, CTransaction &txOut, uint256 &hashBlock); + #endif /* CC_EVAL_H */ diff --git a/src/cc/faucet.cpp b/src/cc/faucet.cpp index 2c0a08451f1..e8c28bb5029 100644 --- a/src/cc/faucet.cpp +++ b/src/cc/faucet.cpp @@ -81,7 +81,7 @@ bool FaucetExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction bool FaucetValidate(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]; - std::vector > txids; + std::vector > addressIndexOutputs; numvins = tx.vin.size(); numvouts = tx.vout.size(); preventCCvins = preventCCvouts = -1; @@ -119,8 +119,8 @@ bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx else if ( (hash[0] & 0xff) != 0 || (hash[31] & 0xff) != 0 ) return eval->Invalid("invalid faucetget txid"); Getscriptaddress(destaddr,tx.vout[i].scriptPubKey); - SetCCtxids(txids,destaddr,tx.vout[i].scriptPubKey.IsPayToCryptoCondition()); - for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) + SetAddressIndexOutputs(addressIndexOutputs,destaddr,tx.vout[i].scriptPubKey.IsPayToCryptoCondition()); + for (std::vector >::const_iterator it=addressIndexOutputs.begin(); it!=addressIndexOutputs.end(); it++) { //int height = it->first.blockHeight; if ( CCduration(numblocks,it->first.txhash) > 0 && numblocks > 3 ) diff --git a/src/cc/fsm.cpp b/src/cc/fsm.cpp index 58b2120cfbe..85510643ff8 100644 --- a/src/cc/fsm.cpp +++ b/src/cc/fsm.cpp @@ -171,7 +171,7 @@ std::string FSMCreate(uint64_t txfee,std::string name,std::string states) if ( CCchange != 0 ) mtx.vout.push_back(MakeCC1vout(EVAL_FSM,CCchange,fsmpk)); mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,opret)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); } else fprintf(stderr,"cant find fsm inputs\n"); return(""); } diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index facedd99659..c2e31587aa8 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -255,15 +255,15 @@ uint8_t games_registeropretdecode(uint256 &gametxid,uint256 &tokenid,uint256 &pl { std::string name, description; std::vector vorigPubkey; std::vector oprets; - std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; + std::vector vextraData, vopret, vopretDummy,origpubkey; uint8_t 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' ) { - GetOpReturnCCBlob(oprets, vopretNonfungible); - vopret = vopretNonfungible; + GetOpReturnCCBlob(oprets, vextraData); + vopret = vextraData; } else if ( script[1] != 'R' && (f= DecodeTokenOpRetV1(scriptPubKey, tokenid, voutPubkeys, oprets)) != 0 ) { @@ -290,21 +290,21 @@ uint8_t games_finishopretdecode(uint256 &gametxid, uint256 &tokenid, int32_t &re std::string name, description; std::vector vorigPubkey; std::vector oprets, opretsDummy; TokenDataTuple tokenData; - std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; + std::vector vextraData, vopret, vopretDummy,origpubkey; uint8_t 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' ) { - GetOpReturnCCBlob(oprets, vopretNonfungible); - vopret = vopretNonfungible; + GetOpReturnCCBlob(oprets, vextraData); + vopret = vextraData; } else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRetV1(scriptPubKey, tokenid, voutPubkeys, opretsDummy)) != 0 ) { //fprintf(stderr,"decode opret %c tokenid.%s\n",script[1],tokenid.GetHex().c_str()); - GetTokenData(reftokenid, tokenData, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx - vopret = vopretNonfungible; + GetTokenData(NULL, reftokenid, tokenData, vextraData); //load nonfungible data from the 'tokenbase' tx + vopret = vextraData; } if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> gametxid; ss >> symbol; ss >> pname; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_GAMES && (f == 'H' || f == 'Q') ) { @@ -1232,7 +1232,7 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); - burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); + burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY_FIXED)); gamespk = GetUnspendable(cp,0); games_univalue(result,"register",-1,-1); playertxid = tokenid = zeroid; @@ -1612,9 +1612,9 @@ UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params,ch opret = games_finishopret(funcid, gametxid, regslot, mypk, newdata,pname); char seedstr[32]; sprintf(seedstr,"%llu",(long long)seed); - std::vector vopretNonfungible; - GetOpReturnData(opret, vopretNonfungible); - rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), std::string(seedstr), gametxid.GetHex(), vopretNonfungible)); + std::vector vextraData; + GetOpReturnData(opret, vextraData); + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), std::string(seedstr), gametxid.GetHex(), vextraData)); } memset(mypriv,0,sizeof(mypriv)); return(games_rawtxresult(result,rawtx,1)); diff --git a/src/cc/heir.cpp b/src/cc/heir.cpp index 33cd7f9d719..c1efb10bca1 100644 --- a/src/cc/heir.cpp +++ b/src/cc/heir.cpp @@ -15,22 +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) -{ - return true; // aleays true for tokel chains -} /* The idea of Heir CC is to allow crypto inheritance. @@ -148,7 +137,7 @@ bool HeirValidate(struct CCcontract_info* cpHeir, Eval* eval, const CTransaction if (fundingTxidInOpret == zeroid) { return eval->Invalid("incorrect tx opreturn: no fundingtxid present"); } - latestTxid = FindLatestFundingTx(fundingTxidInOpret, tokenid, fundingTxOpRetScript, hasHeirSpendingBegun); + latestTxid = FindLatestFundingTx(eval, fundingTxidInOpret, tokenid, fundingTxOpRetScript, hasHeirSpendingBegun); if( tokenid != zeroid && tokenid != tokenidThis ) return eval->Invalid("incorrect tx tokenid"); @@ -336,11 +325,6 @@ uint8_t _DecodeHeirEitherOpRetV1(CScript scriptPubKey, uint256 &tokenid, CPubKey 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 (vopretExtra.size() < 1) { @@ -376,15 +360,12 @@ uint8_t DecodeHeirEitherOpRetV1(CScript scriptPubKey, uint256 &tokenid, uint256 * 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) +uint256 _FindLatestFundingTx(Eval *eval, 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; - - if (!HeirIsVer1Active(NULL)) - return heirv0::_FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, fundingOpretScript, hasHeirSpendingBegun); - + hasHeirSpendingBegun = 0; funcId = 0; @@ -433,7 +414,7 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke int32_t blockHeight = (int32_t)it->second.blockHeight; //NOTE: maybe called from validation code: - if (myGetTransaction(txid, regtx, hash)) { + if (GetTxUnconfirmedOpt(eval, txid, regtx, hash)) { // std::cerr << __func__ << " found tx for txid=" << txid.GetHex() << " blockHeight=" << blockHeight << " maxBlockHeight=" << maxBlockHeight << '\n'; uint256 fundingTxidInOpret; uint256 tokenidInOpret; // not to contaminate the tokenid from the params! @@ -451,7 +432,7 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke bool isNonOwner = false; // 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) + if (TotalPubkeyNormalInputs(eval, regtx, ownerPubkey) > 0 || TotalPubkeyCCInputs(eval, regtx, ownerPubkey) > 0) { // CheckVinPubkey(regtx.vin, ownerPubkey, isOwner, isNonOwner); // if (isOwner && !isNonOwner) { @@ -470,7 +451,7 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke } // overload for validation code -uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &hasHeirSpendingBegun) +uint256 FindLatestFundingTx(Eval *eval, uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &hasHeirSpendingBegun) { uint8_t funcId; CPubKey ownerPubkey; @@ -478,7 +459,7 @@ uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRe int64_t inactivityTime; std::string heirName, memo; - return _FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, opRetScript, hasHeirSpendingBegun); + return _FindLatestFundingTx(eval, fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, opRetScript, hasHeirSpendingBegun); } // overload for transaction creation code @@ -486,7 +467,7 @@ uint256 FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &token { CScript opRetScript; - return _FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, opRetScript, hasHeirSpendingBegun); + return _FindLatestFundingTx(nullptr, fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, opRetScript, hasHeirSpendingBegun); } // add inputs of 1 of 2 cc address @@ -520,7 +501,7 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, if ((txid == fundingtxid || fundingTxidInOpret == fundingtxid) && funcId != 0 && isMyFuncId(funcId) && - (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, voutIndex, tokenid) > 0) && // token validation logic + (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(cp, nullptr, heirtx, voutIndex, tokenid) > 0) && // token validation logic //(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)) { @@ -546,7 +527,7 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' std::vector> addressIndexes; - SetCCtxids(addressIndexes, coinaddr,true); + SetAddressIndexOutputs(addressIndexes, coinaddr, true); int64_t total = 0; for (std::vector>::const_iterator it = addressIndexes.begin(); it != addressIndexes.end(); it++) { @@ -569,7 +550,7 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info if (funcId != 0 && (txid == fundingtxid || fundingTxidInOpret == fundingtxid) && isMyFuncId(funcId) && !isSpendingTx(funcId) && - (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, ivout, tokenid) > 0) && + (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(cp, nullptr, heirtx, ivout, tokenid) > 0) && !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) @@ -647,7 +628,7 @@ template UniValue _HeirFund(int64_t txfee, int64_t amount, std // for initial funding do not allow to sign by non-owner key: // if (hasNotMypubkey) { - if (TotalPubkeyNormalInputs(mtx, myPubkey) < amount && TotalPubkeyCCInputs(mtx, myPubkey) < amount) + if (TotalPubkeyNormalInputs(nullptr, mtx, myPubkey) < amount && TotalPubkeyCCInputs(NULL, mtx, myPubkey) < amount) { result.push_back(Pair("result", "error")); result.push_back(Pair("error", "using non-owner inputs not allowed")); @@ -773,7 +754,8 @@ template UniValue _HeirAdd(uint256 fundingtxid, int64_t txfee, in // 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) { + // TODO: change to Eval instead of NULL + if (TotalPubkeyNormalInputs(nullptr, mtx, myPubkey) < amount && TotalPubkeyCCInputs(NULL, mtx, myPubkey) < amount) { result.push_back(Pair("result", "warning")); result.push_back(Pair("warning", "you are about to make a donation to heir fund")); } diff --git a/src/cc/heir_validate.h b/src/cc/heir_validate.h index cd97bbb69f7..7605d2cbb2f 100644 --- a/src/cc/heir_validate.h +++ b/src/cc/heir_validate.h @@ -5,18 +5,15 @@ #include "CCtokens.h" #include "CCtokens_impl.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); -uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &isHeirSpendingBegan); +uint256 FindLatestFundingTx(Eval *eval, 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); @@ -33,23 +30,13 @@ class CoinHelper { } 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 << EncodeHeirCreateOpRetV1((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 << EncodeHeirOpRetV1((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 << EncodeHeirOpRetV1((uint8_t)'C', fundingtxid, isHeirSpendingBegan); } static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { return MakeCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); @@ -85,22 +72,13 @@ class TokenHelper { } 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 EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, { EncodeHeirCreateOpRetV1((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 EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, { EncodeHeirOpRetV1((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 EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, { EncodeHeirOpRetV1((uint8_t)'C', fundingtxid, isHeirSpendingBegan) }); } static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { diff --git a/src/cc/import.cpp b/src/cc/import.cpp index 206d9e7fd23..a8117275f7b 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -13,14 +13,18 @@ * * ******************************************************************************/ +#include #include "cc/eval.h" #include "cc/utils.h" +#include "komodo_defs.h" #include "importcoin.h" #include "crosschain.h" #include "primitives/transaction.h" #include "cc/CCinclude.h" -#include #include "cc/CCtokens.h" +#include "cc/CCImportGateway.h" +#include "cc/CCupgrades.h" + #include "key_io.h" #define CODA_BURN_ADDRESS "KPrrRoPfHOnNpZZQ6laHXdQDkSQDkVHaN0V+LizLlHxz7NaA59sBAAAA" @@ -33,22 +37,6 @@ ##### 0xffffffff is a special CCid for single chain/dual daemon imports */ -extern std::string ASSETCHAINS_SELFIMPORT; -extern uint16_t ASSETCHAINS_CODAPORT,ASSETCHAINS_BEAMPORT; -extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; -extern uint256 KOMODO_EARLYTXID; - -// utilities from gateways.cpp -uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::vector &txids); -uint256 GatewaysReverseScan(uint256 &txid, int32_t height, uint256 reforacletxid, uint256 batontxid); -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 *nonportable_path(char *str); -char *portable_path(char *str); -void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep); -void *filestr(long *allocsizep,char *_fname); - cJSON* CodaRPC(char **retstr,char const *arg0,char const *arg1,char const *arg2,char const *arg3,char const *arg4,char const *arg5) { char cmdstr[5000],fname[256],*jsonstr; @@ -560,18 +548,23 @@ bool CheckMigration(Eval *eval, const CTransaction &importTx, const CTransaction if (!hasTokenVin) return eval->Invalid("burn-tx-has-no-token-vins"); + std::vector vDeadPubkeys = GetBurnPubKeys(eval->GetCurrentTime(), eval->GetCurrentHeight()); + // calc outputs for burn tx CAmount ccBurnOutputs = 0; for (auto v : burnTx.vout) if (v.scriptPubKey.IsPayToCryptoCondition() && - CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(nonfungibleEvalCode, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY)))) // burned to dead pubkey + std::find_if(vDeadPubkeys.begin(), vDeadPubkeys.end(), [v, nonfungibleEvalCode](const CPubKey &burnpk) { + return IsEqualDestinations(v.scriptPubKey, CCPubKey(CCwrapper(MakeTokensCCcond1(nonfungibleEvalCode, burnpk)).get() )); + } ) != vDeadPubkeys.end()) + //CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(nonfungibleEvalCode, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY_FIXED)))) // burned to dead pubkey ccBurnOutputs += v.nValue; // calc outputs for import tx CAmount ccImportOutputs = 0; for (auto v : importTx.vout) if (v.scriptPubKey.IsPayToCryptoCondition() && - !IsTokenMarkerVout(v)) // should not be marker here + IsTokenMarkerVout(v) > 0LL) // should not be marker here ccImportOutputs += v.nValue; if (ccBurnOutputs != ccImportOutputs) @@ -704,6 +697,8 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp return Invalid("wrong-payouts"); if (targetCcid < KOMODO_FIRSTFUNGIBLEID) return Invalid("chain-not-fungible"); + if (targetSymbol.empty()) + return Invalid("target-symbol-empty"); if ( targetCcid != 0xffffffff ) { diff --git a/src/cc/importgateway.cpp b/src/cc/importgateway.cpp index 782fcc9e6fc..8948ec5c731 100644 --- a/src/cc/importgateway.cpp +++ b/src/cc/importgateway.cpp @@ -13,9 +13,9 @@ * * ******************************************************************************/ -#include "CCImportGateway.h" #include "key_io.h" #include "../importcoin.h" +#include "CCImportGateway.h" // start of consensus code diff --git a/src/cc/includes/cJSON.h b/src/cc/includes/cJSON.h index d919a47a9f2..c16093ff106 100755 --- a/src/cc/includes/cJSON.h +++ b/src/cc/includes/cJSON.h @@ -46,7 +46,7 @@ #include #include -#include "../crypto777/OS_portable.h" +//#include "../crypto777/OS_portable.h" #define SATOSHIDEN ((uint64_t)100000000L) #define dstr(x) ((double)(x) / SATOSHIDEN) diff --git a/src/cc/includes/curve25519.h b/src/cc/includes/curve25519.h index 657fab4d011..96df71cd976 100755 --- a/src/cc/includes/curve25519.h +++ b/src/cc/includes/curve25519.h @@ -55,8 +55,8 @@ void vcalc_sha256(char hashstr[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_ void vcalc_sha256cat(uint8_t hash[256 >> 3],uint8_t *src,int32_t len,uint8_t *src2,int32_t len2); void vupdate_sha256(uint8_t hash[256 >> 3],struct sha256_vstate *state,uint8_t *src,int32_t len); bits256 curve25519_shared(bits256 privkey,bits256 otherpub); -int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); -int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *endianedp); +//int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); +//int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *endianedp); uint32_t calc_crc32(uint32_t crc,const void *buf,size_t size); uint64_t conv_NXTpassword(unsigned char *mysecret,unsigned char *mypublic,uint8_t *pass,int32_t passlen); diff --git a/src/cc/lotto.cpp b/src/cc/lotto.cpp index d7a0b949ac9..5828f322ed0 100644 --- a/src/cc/lotto.cpp +++ b/src/cc/lotto.cpp @@ -316,7 +316,7 @@ std::string LottoTicket(uint64_t txfee,uint256 lottoid,int64_t numtickets) if ( CCchange != 0 ) mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,CCchange,lottopk)); mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,opret)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); } else fprintf(stderr,"cant find Lotto inputs\n"); return(""); } 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 617e232b347..00000000000 --- a/src/cc/old/CCassetstx_v0.cpp +++ /dev/null @@ -1,802 +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; - if (bidamount == 0) { - CCerror = "bid is empty"; - return ""; - } - 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; - if (askamount == 0) { - CCerror = "ask is empty"; - return ""; - } - 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 { - CCerror = "dont have any assets to fill bid"; - return ""; - } - } - else - CCerror = "bid txid not found"; - } - else - CCerror = "no normal coins left"; - return ""; -} - - -// 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.get_msg()); - return(""); - } - if (assetid2 != zeroid) - { - CCerror = "asset swaps disabled"; - fprintf(stderr,"%s\n",CCerror.get_msg()); - 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.get_msg()); - } - } - else - CCerror = "ask txid not found"; - //} - 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 af74e742dc1..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 { - LOGSTREAMFN("cctokens", CCLOG_DEBUG2, stream << "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); - } - } - LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "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) { - LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "incorrect evalcode in tokens opret" << std::endl); - return (uint8_t)0; - } - - funcId = script[1]; - LOGSTREAMFN("cctokens", CCLOG_DEBUG2, stream << "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 - LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "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 - LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "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", CCLOG_DEBUG1, stream << "bad opret format," << " ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); - return (uint8_t)0; - - default: - LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "illegal funcid=" << (int)funcId << std::endl); - return (uint8_t)0; - } - } - else { - LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "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 398212bd8d8..4cca6ebb088 100644 --- a/src/cc/oracles.cpp +++ b/src/cc/oracles.cpp @@ -13,6 +13,7 @@ * * ******************************************************************************/ +#include "komodo_defs.h" #include "CCOracles.h" #include @@ -92,213 +93,211 @@ vout.n-1: opreturn with oracletxid, prevbatontxid and data in proper format */ -extern int32_t komodo_get_current_height(); #define PUBKEY_SPOOFING_FIX_ACTIVATION 1563148800 #define CC_MARKER_VALUE 10000 -#define CC_TXFEE 10000 +#define CC_TXFEE 10000 // start of consensus code -CScript EncodeOraclesCreateOpRet(uint8_t funcid,std::string name,std::string description,std::string format) +CScript EncodeOraclesCreateOpRet(uint8_t funcid, std::string name, std::string description, std::string format) { - CScript opret; uint8_t evalcode = EVAL_ORACLES; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << name << format << description); - return(opret); + CScript opret; + uint8_t evalcode = EVAL_ORACLES; + uint8_t version = 1; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << version << name << format << description); + return opret; } -uint8_t DecodeOraclesCreateOpRet(const CScript &scriptPubKey,std::string &name,std::string &description,std::string &format) +uint8_t DecodeOraclesCreateOpRet(const CScript& scriptPubKey, std::string& name, std::string& description, std::string& format) { - std::vector vopret; uint8_t *script,e,f,funcid; - GetOpReturnData(scriptPubKey,vopret); - script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0] == EVAL_ORACLES ) - { - if ( script[1] == 'C' ) - { - if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> name; ss >> format; ss >> description) != 0 ) - { - return(script[1]); - } else fprintf(stderr,"DecodeOraclesCreateOpRet unmarshal error for C\n"); + std::vector vopret; + uint8_t e, f, funcid, version; + GetOpReturnData(scriptPubKey, vopret); + if (vopret.size() > 2 && vopret[0] == EVAL_ORACLES) { + if (vopret[1] == 'C') { + if (E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> version; ss >> name; ss >> format; ss >> description) != 0) { + return vopret[1]; + } else + fprintf(stderr, "DecodeOraclesCreateOpRet unmarshal error for C\n"); } } - return(0); + return 0; } -CScript EncodeOraclesOpRet(uint8_t funcid,uint256 oracletxid,CPubKey pk,int64_t num) +CScript EncodeOraclesOpRet(uint8_t funcid, uint256 oracletxid, CPubKey pk, int64_t num) { - CScript opret; uint8_t evalcode = EVAL_ORACLES; + CScript opret; + uint8_t evalcode = EVAL_ORACLES; + uint8_t version = 1; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << oracletxid << pk << num); - return(opret); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << version << oracletxid << pk << num); + return opret; } -uint8_t DecodeOraclesOpRet(const CScript &scriptPubKey,uint256 &oracletxid,CPubKey &pk,int64_t &num) +uint8_t DecodeOraclesOpRet(const CScript& scriptPubKey, uint256& oracletxid, CPubKey& pk, int64_t& num) { - std::vector vopret; uint8_t *script,e,f; + std::vector vopret; + uint8_t e, f, version; - GetOpReturnData(scriptPubKey,vopret); - script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0] == EVAL_ORACLES ) - { - if (script[0] == EVAL_ORACLES && (script[1]== 'R' || script[1] == 'S' || script[1] == 'F') && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> oracletxid; ss >> pk; ss >> num)!=0) - return(f); - else return(script[1]); + GetOpReturnData(scriptPubKey, vopret); + if (vopret.size() > 2 && vopret[0] == EVAL_ORACLES) { + if (vopret[0] == EVAL_ORACLES && (vopret[1] == 'R' || vopret[1] == 'S' || vopret[1] == 'F') && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> version; ss >> oracletxid; ss >> pk; ss >> num) != 0) + return f; + else + return vopret[1]; } - return(0); + return 0; } -CScript EncodeOraclesData(uint8_t funcid,uint256 oracletxid,uint256 batontxid,CPubKey pk,std::vector data) +CScript EncodeOraclesData(uint8_t funcid, uint256 oracletxid, uint256 batontxid, CPubKey pk, std::vector data) { - CScript opret; uint8_t evalcode = EVAL_ORACLES; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << oracletxid << batontxid << pk << data); - return(opret); + CScript opret; + uint8_t evalcode = EVAL_ORACLES; + uint8_t version = 1; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << version << oracletxid << batontxid << pk << data); + return (opret); } -uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint256 &batontxid,CPubKey &pk,std::vector &data) +uint8_t DecodeOraclesData(const CScript& scriptPubKey, uint256& oracletxid, uint256& batontxid, CPubKey& pk, std::vector& data) { - 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 >> oracletxid; ss >> batontxid; ss >> pk; ss >> data) != 0 ) - { - if ( e == EVAL_ORACLES && f == 'D' ) - return(f); + std::vector vopret; + uint8_t e, f, version; + GetOpReturnData(scriptPubKey, vopret); + if (vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> version; ss >> oracletxid; ss >> batontxid; ss >> pk; ss >> data) != 0) { + if (e == EVAL_ORACLES && f == 'D') + return (f); //else fprintf(stderr,"DecodeOraclesData evalcode.%d f.%c\n",e,f); } //else fprintf(stderr,"DecodeOraclesData not enough opereturn data\n"); - return(0); + return (0); } -CPubKey OracleBatonPk(char *batonaddr,struct CCcontract_info *cp) +CPubKey OracleBatonPk(char* batonaddr, struct CCcontract_info* cp) { - static secp256k1_context *ctx; + static secp256k1_context* ctx; size_t clen = CPubKey::PUBLIC_KEY_SIZE; - secp256k1_pubkey pubkey; CPubKey batonpk; uint8_t priv[32]; int32_t i; - if ( ctx == 0 ) + secp256k1_pubkey pubkey; + CPubKey batonpk; + uint8_t priv[32]; + if (ctx == 0) ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); Myprivkey(priv); cp->unspendableEvalcode2 = EVAL_ORACLES; - for (i=0; i<32; i++) + for (int32_t i = 0; i < 32; i++) cp->unspendablepriv2[i] = (priv[i] ^ cp->CCpriv[i]); - while ( secp256k1_ec_seckey_verify(ctx,cp->unspendablepriv2) == 0 ) - { + while (secp256k1_ec_seckey_verify(ctx, cp->unspendablepriv2) == 0) { // 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 ) + fprintf(stderr, " invalid privkey\n"); + if (secp256k1_ec_privkey_tweak_add(ctx, cp->unspendablepriv2, priv) != 0) break; } - if ( secp256k1_ec_pubkey_create(ctx,&pubkey,cp->unspendablepriv2) != 0 ) - { - secp256k1_ec_pubkey_serialize(ctx,(unsigned char*)batonpk.begin(),&clen,&pubkey,SECP256K1_EC_COMPRESSED); + if (secp256k1_ec_pubkey_create(ctx, &pubkey, cp->unspendablepriv2) != 0) { + secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)batonpk.begin(), &clen, &pubkey, SECP256K1_EC_COMPRESSED); cp->unspendablepk2 = batonpk; - Getscriptaddress(batonaddr,MakeCC1vout(cp->evalcode,0,batonpk).scriptPubKey); + Getscriptaddress(batonaddr, MakeCC1vout(cp->evalcode, 0, batonpk).scriptPubKey); //fprintf(stderr,"batonpk.(%s) -> %s\n",(char *)HexStr(batonpk).c_str(),batonaddr); - strcpy(cp->unspendableaddr2,batonaddr); - } else fprintf(stderr,"error creating pubkey\n"); - memset(priv,0,sizeof(priv)); - return(batonpk); + strcpy(cp->unspendableaddr2, batonaddr); + } else + fprintf(stderr, "error creating pubkey\n"); + memset(priv, 0, sizeof(priv)); + return (batonpk); } -int64_t OracleCurrentDatafee(uint256 reforacletxid,char *markeraddr,CPubKey publisher) +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; + uint256 txid, oracletxid, hashBlock; + int64_t datafee = 0, dfee; + int32_t dheight = 0, vout, height; + CTransaction tx; + CPubKey pk; + std::vector> unspentOutputs; + struct CCcontract_info *cp, C; - cp = CCinit(&C,EVAL_ORACLES); - SetCCunspents(unspentOutputs,markeraddr,false); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { + cp = CCinit(&C, EVAL_ORACLES); + 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 ( FetchCCtx(txid,tx,cp) && (numvouts = tx.vout.size()) > 0 ) // version1 is true for tokel + if (FetchCCtx(txid, tx, cp) && tx.vout.size() > 0) // version1 is true for tokel { - if ( DecodeOraclesOpRet(tx.vout[numvouts-1].scriptPubKey,oracletxid,pk,dfee) == 'R' ) - { - if ( oracletxid == reforacletxid && pk == publisher ) - { - if ( height > dheight || (height == dheight && dfee < datafee) ) - { + if (DecodeOraclesOpRet(tx.vout.back().scriptPubKey, oracletxid, pk, dfee) == 'R') { + if (oracletxid == reforacletxid && pk == publisher) { + if (height > dheight || (height == dheight && dfee < datafee)) { dheight = height; datafee = dfee; - if ( 0 && dheight != 0 ) - fprintf(stderr,"set datafee %.8f height.%d\n",(double)datafee/COIN,height); + if (0 && dheight != 0) + fprintf(stderr, "set datafee %.8f height.%d\n", (double)datafee / COIN, height); } } } } } - return(datafee); + return (datafee); } -int64_t OracleDatafee(CScript &scriptPubKey,uint256 oracletxid,CPubKey publisher) +int64_t OracleDatafee(CScript& scriptPubKey, uint256 oracletxid, CPubKey publisher) { - CTransaction oracletx; char markeraddr[64]; uint256 hashBlock; std::string name,description,format; int32_t numvouts; int64_t datafee = 0; - if ( myGetTransaction(oracletxid,oracletx,hashBlock) != 0 && (numvouts= oracletx.vout.size()) > 0 ) - { - if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) == 'C' ) - { - CCtxidaddr(markeraddr,oracletxid); - datafee = OracleCurrentDatafee(oracletxid,markeraddr,publisher); - } - else - { - fprintf(stderr,"Could not decode op_ret from transaction %s\nscriptPubKey: %s\n", oracletxid.GetHex().c_str(), oracletx.vout[numvouts-1].scriptPubKey.ToString().c_str()); + CTransaction oracletx; + char markeraddr[64]; + uint256 hashBlock; + std::string name, description, format; + int64_t datafee = 0; + if (myGetTransaction(oracletxid, oracletx, hashBlock) && oracletx.vout.size() > 0) { + if (DecodeOraclesCreateOpRet(oracletx.vout.back().scriptPubKey, name, description, format) == 'C') { + CCtxidaddr(markeraddr, oracletxid); + datafee = OracleCurrentDatafee(oracletxid, markeraddr, publisher); + } else { + fprintf(stderr, "Could not decode op_ret from transaction %s\nscriptPubKey: %s\n", oracletxid.GetHex().c_str(), oracletx.vout.back().scriptPubKey.ToString().c_str()); } } - return(datafee); + return (datafee); } -static uint256 myIs_baton_spentinmempool(uint256 batontxid,int32_t batonvout) +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++) - { - const CTransaction &tx = *it; - if ( tx.vout.size() > 0 && tx.vin.size() > 1 && batontxid == tx.vin[1].prevout.hash && batonvout == tx.vin[1].prevout.n ) - { - const uint256 &txid = tx.GetHash(); + myGet_mempool_txs(tmp_txs, EVAL_ORACLES, 'D'); + for (std::vector::const_iterator it = tmp_txs.begin(); it != tmp_txs.end(); it++) { + const CTransaction& tx = *it; + if (tx.vout.size() > 0 && tx.vin.size() > 1 && batontxid == tx.vin[1].prevout.hash && batonvout == tx.vin[1].prevout.n) { + const uint256& txid = tx.GetHash(); //char str[65]; fprintf(stderr,"found baton spent in mempool %s\n",uint256_str(str,txid)); - return(txid); + return (txid); } } - return(batontxid); + return (batontxid); } -uint256 OracleBatonUtxo(uint64_t value,struct CCcontract_info *cp,uint256 reforacletxid,char *batonaddr,CPubKey publisher,std::vector &dataarg) +uint256 OracleBatonUtxo(uint64_t value, 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; - std::vector > unspentOutputs; + uint256 txid, oracletxid, hashBlock, btxid, batontxid = zeroid; + int64_t dfee; + int32_t dheight = 0, vout, height; + CTransaction tx; + CPubKey pk; + std::vector vopret, data; + std::vector> unspentOutputs; - SetCCunspents(unspentOutputs,batonaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { + 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 != value) { //fprintf(stderr,"it->second.satoshis %llu != %llu txfee\n",(long long)it->second.satoshis,(long long)value); continue; } - if ( FetchCCtx(txid,tx,cp) && (numvouts= tx.vout.size()) > 0 ) - { - GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); - if ( vopret.size() > 2 ) - { - ptr = (uint8_t *)vopret.data(); - if ( (ptr[1] == 'D' && DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D') || (ptr[1] == 'R' && DecodeOraclesOpRet(tx.vout[numvouts-1].scriptPubKey,oracletxid,pk,dfee) == 'R') ) - { - if ( oracletxid == reforacletxid && pk == publisher ) - { - if ( height > dheight ) - { + if (FetchCCtx(txid, tx, cp) && tx.vout.size() > 0) { + GetOpReturnData(tx.vout.back().scriptPubKey, vopret); + if (vopret.size() > 2) { + if ((vopret[1] == 'D' && DecodeOraclesData(tx.vout.back().scriptPubKey, oracletxid, btxid, pk, data) == 'D') || (vopret[1] == 'R' && DecodeOraclesOpRet(tx.vout.back().scriptPubKey, oracletxid, pk, dfee) == 'R')) { + if (oracletxid == reforacletxid && pk == publisher) { + if (height > dheight) { dheight = height; batontxid = txid; - if ( ptr[1] == 'D' ) + if (vopret[1] == 'D') dataarg = data; //char str[65]; fprintf(stderr,"set batontxid %s height.%d\n",uint256_str(str,batontxid),height); } @@ -307,40 +306,43 @@ uint256 OracleBatonUtxo(uint64_t value,struct CCcontract_info *cp,uint256 refora } } } - while ( myIsutxo_spentinmempool(ignoretxid,ignorevin,batontxid,1) != 0 ) - batontxid = myIs_baton_spentinmempool(batontxid,1); - return(batontxid); + while (myIsutxo_spentinmempool(ignoretxid, ignorevin, batontxid, 1) != 0) + batontxid = myIs_baton_spentinmempool(batontxid, 1); + return (batontxid); } -uint256 OraclesBatontxid(uint256 reforacletxid,CPubKey refpk) +uint256 OraclesBatontxid(uint256 reforacletxid, CPubKey refpk) { - std::vector > unspentOutputs; - CTransaction regtx; uint256 hash,txid,batontxid,oracletxid; CPubKey pk; int32_t numvouts,height,maxheight=0; int64_t datafee; char markeraddr[64],batonaddr[64]; std::vector data; struct CCcontract_info *cp,C; + std::vector> unspentOutputs; + CTransaction regtx; + uint256 hash, txid, batontxid, oracletxid; + CPubKey pk; + int32_t height, maxheight = 0; + int64_t datafee; + char markeraddr[64], batonaddr[64]; + std::vector data; + struct CCcontract_info *cp, C; batontxid = zeroid; - cp = CCinit(&C,EVAL_ORACLES); - CCtxidaddr(markeraddr,reforacletxid); - SetCCunspents(unspentOutputs,markeraddr,false); + cp = CCinit(&C, EVAL_ORACLES); + CCtxidaddr(markeraddr, reforacletxid); + SetCCunspents(unspentOutputs, markeraddr, false); //char str[67]; fprintf(stderr,"markeraddr.(%s) %s\n",markeraddr,pubkey33_str(str,(uint8_t *)&refpk)); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { + for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { 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 ( 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); + if (FetchCCtx(txid, regtx, cp)) { + if (regtx.vout.size() >= 2 && DecodeOraclesOpRet(regtx.vout.back().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); break; } } } - return(batontxid); + return (batontxid); } - /*int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format) { std::vector > unspentOutputs; @@ -380,73 +382,65 @@ uint256 OraclesBatontxid(uint256 reforacletxid,CPubKey refpk) return(0); }*/ -int64_t IsOraclesvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) +int64_t IsOraclesvout(struct CCcontract_info* cp, const CTransaction& tx, int32_t v) { //char destaddr[64]; - if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) - { + if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) { //if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) - return(tx.vout[v].nValue); + return (tx.vout[v].nValue); } - return(0); + return (0); } -bool OraclesDataValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,uint256 oracletxid,CPubKey publisher,int64_t datafee) +bool OraclesDataValidate(struct CCcontract_info* cp, Eval* eval, const CTransaction& tx, uint256 oracletxid, CPubKey publisher, int64_t datafee) { static uint256 zerohash; - CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; CScript scriptPubKey; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - if ( OracleDatafee(scriptPubKey,oracletxid,publisher) != datafee ) + CTransaction vinTx; + uint256 hashBlock, activehash; + int64_t inputs = 0, outputs = 0, assetoshis; + CScript scriptPubKey; + if (OracleDatafee(scriptPubKey, oracletxid, publisher) != datafee) return eval->Invalid("mismatched datafee"); - scriptPubKey = MakeCC1vout(cp->evalcode,0,publisher).scriptPubKey; - for (i=0; ievalcode, 0, publisher).scriptPubKey; + for (int32_t i = 0; i < tx.vin.size(); i++) { //fprintf(stderr,"vini.%d\n",i); - if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) - { - if ( i == 0 ) + if ((*cp->ismyvin)(tx.vin[i].scriptSig) != 0) { + if (i == 0) return eval->Invalid("unexpected vin.0 is CC"); //fprintf(stderr,"vini.%d check mempool\n",i); - else if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) + else if (eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) return eval->Invalid("cant find vinTx"); - else - { + else { //fprintf(stderr,"vini.%d check hash and vout\n",i); //if ( hashBlock == zerohash ) // return eval->Invalid("cant Oracles from mempool"); - if ( (assetoshis= IsOraclesvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) - { - if ( i == 1 && vinTx.vout[1].scriptPubKey != tx.vout[1].scriptPubKey ) + if ((assetoshis = IsOraclesvout(cp, vinTx, tx.vin[i].prevout.n)) != 0) { + if (i == 1 && vinTx.vout[1].scriptPubKey != tx.vout[1].scriptPubKey) return eval->Invalid("baton violation"); - else if ( i != 1 && scriptPubKey == vinTx.vout[tx.vin[i].prevout.n].scriptPubKey ) + else if (i != 1 && scriptPubKey == vinTx.vout[tx.vin[i].prevout.n].scriptPubKey) inputs += assetoshis; } } } } - for (i=0; iInvalid("invalid CC vout CC destination"); + else + return eval->Invalid("invalid CC vout CC destination"); } } } } - if ( inputs != outputs+datafee ) - { - fprintf(stderr,"inputs %llu vs outputs %llu + datafee %llu\n",(long long)inputs,(long long)outputs,(long long)datafee); + if (inputs != outputs + datafee) { + fprintf(stderr, "inputs %llu vs outputs %llu + datafee %llu\n", (long long)inputs, (long long)outputs, (long long)datafee); return eval->Invalid("mismatched inputs != outputs + datafee"); - } - else return(true); + } else + return true; } /*nt32_t GetLatestTimestamp(int32_t height) @@ -455,566 +449,590 @@ bool OraclesDataValidate(struct CCcontract_info *cp,Eval* eval,const CTransactio return(komodo_heightstamp(height)); } */ -bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) +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; - 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; + uint256 oracletxid, batontxid, txid; + int32_t preventCCvins, preventCCvouts; + int64_t amount; + uint256 hashblock; + std::vector vopret; + std::vector data; + + CPubKey publisher, tmppk, oraclespk; + char tmpaddress[64], vinaddress[64], oraclesaddr[64]; + CTransaction tmptx; + std::string name, desc, format; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) + if (tx.vout.size() < 1) return eval->Invalid("no vouts"); - else - { + else { //if (GetLatestTimestamp(komodo_currentheight())>=JUNE2021_NNELECTION_HARDFORK) if (true) // always true for tokel { - CCOpretCheck(eval,tx,true,true,true); - ExactAmounts(eval,tx,CC_TXFEE); + CCOpretCheck(eval, tx, true, true, true); + ExactAmounts(eval, tx, CC_TXFEE); } - GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); - if ( vopret.size() > 2 ) - { - oraclespk=GetUnspendable(cp,0); - Getscriptaddress(oraclesaddr,CScript() << ParseHex(HexStr(oraclespk)) << OP_CHECKSIG); - script = (uint8_t *)vopret.data(); - switch ( script[1] ) - { - case 'C': // create - // vins.*: normal inputs - // vout.0: marker 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.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 - // 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 - // vout.n-1: opreturn with createtxid, pubkey and price per data point - if (GetLatestTimestamp(eval->GetCurrentHeight())>PUBKEY_SPOOFING_FIX_ACTIVATION) - { - if ((numvouts=tx.vout.size()) < 1 || DecodeOraclesOpRet(tx.vout[numvouts-1].scriptPubKey,oracletxid,tmppk,amount)!='R') - return eval->Invalid("invalid oraclesregister OP_RETURN data!"); - else if (myGetTransaction(oracletxid,tmptx,hashblock) == 0) - return eval->Invalid("invalid oraclescreate txid!"); - else if ((numvouts=tmptx.vout.size()) < 1 || DecodeOraclesCreateOpRet(tmptx.vout[numvouts-1].scriptPubKey,name,desc,format)!='C') - 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) - 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) - 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!"); - } - else return eval->Invalid("unexpected OraclesValidate for register"); - break; - case 'S': // subscribe - // vins.*: normal inputs - // vout.0: subscription fee to publishers CC address - // vout.1: change, if any - // vout.n-1: opreturn with createtxid, registered provider's pubkey, amount - return eval->Invalid("unexpected OraclesValidate for subscribe"); - break; - case 'D': // data - // vin.0: normal input - // vin.1: baton CC utxo (most of the time) - // vin.2+: subscription vout.0 - // vout.0: change to publishers CC address - // vout.1: baton CC utxo - // vout.2: payment for dataprovider - // vout.3: change, if any - if ( numvins >= 2 && numvouts >= 3 && DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,batontxid,publisher,data) == 'D' ) - { - if ( OraclesDataValidate(cp,eval,tx,oracletxid,publisher,tx.vout[2].nValue) != 0 ) - { - return(true); - } else return(false); - } - return eval->Invalid("unexpected OraclesValidate 'D' tx invalid"); - break; - default: - fprintf(stderr,"illegal oracles funcid.(%c)\n",script[1]); - return eval->Invalid("unexpected OraclesValidate funcid"); - break; + GetOpReturnData(tx.vout.back().scriptPubKey, vopret); + if (vopret.size() > 2) { + oraclespk = GetUnspendable(cp, 0); + Getscriptaddress(oraclesaddr, CScript() << ParseHex(HexStr(oraclespk)) << OP_CHECKSIG); + switch (vopret[1]) { + case 'C': // create + // vins.*: normal inputs + // vout.0: marker 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"); + 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.1: change, if any + // vout.n-1: opreturn with createtxid, pubkey and amount + return eval->Invalid("unexpected OraclesValidate for create"); + 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 + // 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 + // vout.n-1: opreturn with createtxid, pubkey and price per data point + if (GetLatestTimestamp(eval->GetCurrentHeightCompat()) > PUBKEY_SPOOFING_FIX_ACTIVATION) { + if (tx.vout.size() < 1 || DecodeOraclesOpRet(tx.vout.back().scriptPubKey, oracletxid, tmppk, amount) != 'R') + return eval->Invalid("invalid oraclesregister OP_RETURN data!"); + else if (!eval->GetTxUnconfirmed(oracletxid, tmptx, hashblock)) + return eval->Invalid("invalid oraclescreate txid!"); + else if (tmptx.vout.size() < 1 || DecodeOraclesCreateOpRet(tmptx.vout.back().scriptPubKey, name, desc, format) != 'C') + 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) == false) + return eval->Invalid("invalid marker for oraclescreate!"); + else if (tx.vin.size() < 2) + return eval->Invalid("vin number too low"); + 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) + 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!"); + } else + return eval->Invalid("unexpected OraclesValidate for register"); + break; + case 'S': // subscribe + // vins.*: normal inputs + // vout.0: subscription fee to publishers CC address + // vout.1: change, if any + // vout.n-1: opreturn with createtxid, registered provider's pubkey, amount + return eval->Invalid("unexpected OraclesValidate for subscribe"); + case 'D': // data + // vin.0: normal input + // vin.1: baton CC utxo (most of the time) + // vin.2+: subscription vout.0 + // vout.0: change to publishers CC address + // vout.1: baton CC utxo + // vout.2: payment for dataprovider + // vout.3: change, if any + if (tx.vin.size() >= 2 && tx.vout.size() >= 3 && DecodeOraclesData(tx.vout.back().scriptPubKey, oracletxid, batontxid, publisher, data) == 'D') { + if (OraclesDataValidate(cp, eval, tx, oracletxid, publisher, tx.vout[2].nValue) != 0) { + return true; + } else + return false; + } + return eval->Invalid("unexpected OraclesValidate 'D' tx invalid"); + default: + fprintf(stderr, "illegal oracles funcid.(%c)\n", vopret[1]); + return eval->Invalid("unexpected OraclesValidate funcid"); } - } else return eval->Invalid("unexpected oracles missing funcid"); - return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts)); + } else + return eval->Invalid("unexpected oracles missing funcid"); + return PreventCC(eval, tx, preventCCvins, tx.vin.size(), preventCCvouts, tx.vout.size()); } - return(true); + return true; } // end of consensus code // helper functions for rpc calls in rpcwallet.cpp -int64_t AddOracleInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,uint256 oracletxid,CPubKey pk,int64_t total,int32_t maxinputs) +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; - std::vector > unspentOutputs; CPubKey tmppk; int64_t tmpnum; + char coinaddr[64], funcid; + int64_t nValue, price, totalinputs = 0; + uint256 tmporacletxid, tmpbatontxid, txid, hashBlock; + std::vector origpubkey, data; + CTransaction oracletx; + int32_t vout, n = 0; + std::vector> unspentOutputs; + CPubKey tmppk; + int64_t tmpnum; - GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr,true); + GetCCaddress(cp, coinaddr, pk); + SetCCunspents(unspentOutputs, coinaddr, true); //fprintf(stderr,"addoracleinputs from (%s)\n",coinaddr); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { + for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; //char str[65]; fprintf(stderr,"oracle check %s/v%d\n",uint256_str(str,txid),vout); - if ( myGetTransaction(txid,vintx,hashBlock) != 0 && (numvouts=vintx.vout.size()-1)>0) - { - 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) - fprintf(stderr,"invalid oraclesdata transaction \n"); - else if (tmporacletxid==oracletxid) - { + if (myGetTransaction(txid, oracletx, hashBlock) && oracletx.vout.size() > 1) { + if ((funcid = DecodeOraclesOpRet(oracletx.vout.back().scriptPubKey, tmporacletxid, tmppk, tmpnum)) != 0 && (funcid == 'S' || funcid == 'D')) { + if (funcid == 'D' && ValidateCCtx(oracletx, cp) && DecodeOraclesData(oracletx.vout.back().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 ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,vout,CScript())); + if ((nValue = IsOraclesvout(cp, oracletx, vout)) >= CC_MARKER_VALUE && 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) ) + if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break; } //else fprintf(stderr,"nValue %.8f or utxo memspent\n",(double)nValue/COIN); - } + } } - } else fprintf(stderr,"couldnt find transaction\n"); + } else + fprintf(stderr, "couldnt find transaction\n"); } - return(totalinputs); + return (totalinputs); } -int64_t LifetimeOraclesFunds(struct CCcontract_info *cp,uint256 oracletxid,CPubKey publisher) +int64_t LifetimeOraclesFunds(struct CCcontract_info* cp, uint256 oracletxid, CPubKey publisher) { - char coinaddr[64]; CPubKey pk; int64_t total=0,num; uint256 txid,hashBlock,subtxid; CTransaction subtx; + 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'); + GetCCaddress(cp, coinaddr, publisher); + SetCCtxids(txids, coinaddr, true, cp->evalcode, 0, oracletxid, 'S'); //fprintf(stderr,"scan lifetime of %s\n",coinaddr); - for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) - { + for (std::vector::const_iterator it = txids.begin(); it != txids.end(); it++) { txid = *it; - if ( myGetTransaction(txid,subtx,hashBlock) != 0 ) - { - if ( subtx.vout.size() > 0 && DecodeOraclesOpRet(subtx.vout[subtx.vout.size()-1].scriptPubKey,subtxid,pk,num) == 'S' && subtxid == oracletxid && pk == publisher ) - { + if (myGetTransaction(txid, subtx, hashBlock) != 0) { + if (subtx.vout.size() > 0 && DecodeOraclesOpRet(subtx.vout[subtx.vout.size() - 1].scriptPubKey, subtxid, pk, num) == 'S' && subtxid == oracletxid && pk == publisher) { total += subtx.vout[0].nValue; } } } - return(total); + return (total); } -int64_t AddMyOraclesFunds(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 oracletxid) +int64_t AddMyOraclesFunds(struct CCcontract_info* cp, CMutableTransaction& mtx, CPubKey pk, uint256 oracletxid) { - char coinaddr[64],funcid; int64_t nValue,tmpamount; uint256 tmporacletxid,txid,hashBlock,ignoretxid; int32_t numvouts,vout,ignorevin; - std::vector > unspentOutputs; CTransaction vintx; CPubKey tmppk; + char coinaddr[64], funcid; + int64_t nValue, tmpamount; + uint256 tmporacletxid, txid, hashBlock, ignoretxid; + int32_t vout, ignorevin; + std::vector> unspentOutputs; + CTransaction vintx; + CPubKey tmppk; - GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { + GetCCaddress(cp, coinaddr, pk); + 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 = it->second.satoshis; - if ( myGetTransaction(txid,vintx,hashBlock) != 0 && (numvouts=vintx.vout.size())>0) - { - if ((funcid=DecodeOraclesOpRet(vintx.vout[numvouts-1].scriptPubKey,tmporacletxid,tmppk,tmpamount))!=0 && funcid=='F' && tmppk==pk - && tmporacletxid==oracletxid && tmpamount==nValue && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout)==0) - { - mtx.vin.push_back(CTxIn(txid,vout,CScript())); + if (myGetTransaction(txid, vintx, hashBlock) != 0 && vintx.vout.size() > 0) { + if ((funcid = DecodeOraclesOpRet(vintx.vout.back().scriptPubKey, tmporacletxid, tmppk, tmpamount)) != 0 && funcid == 'F' && tmppk == pk && tmporacletxid == oracletxid && tmpamount == nValue && myIsutxo_spentinmempool(ignoretxid, ignorevin, txid, vout) == 0) { + mtx.vin.push_back(CTxIn(txid, vout, CScript())); return (nValue); } - } else fprintf(stderr,"couldnt find transaction\n"); + } else + fprintf(stderr, "couldnt find transaction\n"); } std::vector tmp_txs; - myGet_mempool_txs(tmp_txs,EVAL_ORACLES,'F'); - for (std::vector::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++) - { - const CTransaction &txmempool = *it; - const uint256 &hash = txmempool.GetHash(); - nValue=txmempool.vout[0].nValue; + myGet_mempool_txs(tmp_txs, EVAL_ORACLES, 'F'); + for (std::vector::const_iterator it = tmp_txs.begin(); it != tmp_txs.end(); it++) { + const CTransaction& txmempool = *it; + const uint256& hash = txmempool.GetHash(); + nValue = txmempool.vout[0].nValue; - if ((funcid=DecodeOraclesOpRet(txmempool.vout[txmempool.vout.size()-1].scriptPubKey,tmporacletxid,tmppk,tmpamount))!=0 && funcid=='F' - && tmppk==pk && tmporacletxid==oracletxid && tmpamount==nValue && myIsutxo_spentinmempool(ignoretxid,ignorevin,hash,0)==0) - { - mtx.vin.push_back(CTxIn(hash,0,CScript())); + if ((funcid = DecodeOraclesOpRet(txmempool.vout[txmempool.vout.size() - 1].scriptPubKey, tmporacletxid, tmppk, tmpamount)) != 0 && funcid == 'F' && tmppk == pk && tmporacletxid == oracletxid && tmpamount == nValue && myIsutxo_spentinmempool(ignoretxid, ignorevin, hash, 0) == 0) { + mtx.vin.push_back(CTxIn(hash, 0, CScript())); return (nValue); } } return (0); } -UniValue OracleCreate(const CPubKey& pk, int64_t txfee,std::string name,std::string description,std::string format) +UniValue OracleCreate(const CPubKey& pk, int64_t txfee, std::string name, std::string description, std::string format) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,Oraclespk; struct CCcontract_info *cp,C; char fmt; + CPubKey mypk, Oraclespk; + struct CCcontract_info *cp, C; + char fmt; - cp = CCinit(&C,EVAL_ORACLES); - if ( name.size() > 32) - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "name."<< (int32_t)name.size() << " must be less then 32"); + cp = CCinit(&C, EVAL_ORACLES); + if (name.size() > 32) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "name." << (int32_t)name.size() << " must be less then 32"); if (description.size() > 4096) - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "description."<< (int32_t)description.size() << " must be less then 4096"); - if (format.size() > 4096 ) - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "format."<< (int32_t)format.size() << " must be less then 4096"); - if ( name.size() == 0 ) - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "name must not be empty"); - for(int i = 0; i < format.size(); i++) - { - fmt=format[i]; - switch (fmt) - { - case 's': case 'S': case 'd': case 'D': - case 'c': case 'C': case 't': case 'T': - case 'i': case 'I': case 'l': case 'L': - case 'h': break; - default: CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "invalid format type"); + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "description." << (int32_t)description.size() << " must be less then 4096"); + if (format.size() > 4096) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "format." << (int32_t)format.size() << " must be less then 4096"); + if (name.size() == 0) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "name must not be empty"); + for (int i = 0; i < format.size(); i++) { + fmt = format[i]; + switch (fmt) { + case 's': + case 'S': + case 'd': + case 'D': + case 'c': + case 'C': + case 't': + case 'T': + case 'i': + case 'I': + case 'l': + case 'L': + case 'h': + break; + default: + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "invalid format type"); } - } - if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_ORACLES]?0:CC_TXFEE; - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - Oraclespk = GetUnspendable(cp,0); - if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0 ) - { - mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,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"); + if (txfee == 0) + txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_ORACLES] ? 0 : CC_TXFEE; + mypk = pk.IsValid() ? pk : pubkey2pk(Mypubkey()); + Oraclespk = GetUnspendable(cp, 0); + if (AddNormalinputs(mtx, mypk, txfee + CC_MARKER_VALUE, 0x1000, pk.IsValid()) > 0) { + mtx.vout.push_back(CTxOut(CC_MARKER_VALUE, CScript() << ParseHex(HexStr(Oraclespk)) << OP_CHECKSIG)); + return (FinalizeCCTxExt(pk.IsValid(), FINALIZECCTX_NO_CHANGE_WHEN_DUST, cp, mtx, mypk, txfee, EncodeOraclesCreateOpRet('C', name, description, format))); + } + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "error adding normal inputs"); } -UniValue OracleFund(const CPubKey& pk, int64_t txfee,uint256 oracletxid) +UniValue OracleFund(const CPubKey& pk, int64_t txfee, uint256 oracletxid) { - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction tx; - CPubKey mypk,oraclespk; struct CCcontract_info *cp,C; std::string name,desc,format; int32_t numvouts; uint256 hashBlock; - - if (GetLatestTimestamp(komodo_currentheight())evalcode,CC_MARKER_VALUE,mypk)); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeOraclesOpRet('F',oracletxid,mypk,CC_MARKER_VALUE))); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction tx; + CPubKey mypk, oraclespk; + struct CCcontract_info *cp, C; + std::string name, desc, format; + uint256 hashBlock; + + if (GetLatestTimestamp(komodo_currentheight()) < PUBKEY_SPOOFING_FIX_ACTIVATION) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "oraclesfund not active yet, activation scheduled for July 15th"); + if (!myGetTransaction(oracletxid, tx, hashBlock) || tx.vout.size() <= 0) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); + if (DecodeOraclesCreateOpRet(tx.vout.back().scriptPubKey, name, desc, format) != 'C') + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); + cp = CCinit(&C, EVAL_ORACLES); + if (txfee == 0) + txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_ORACLES] ? 0 : CC_TXFEE; + mypk = pk.IsValid() ? pk : pubkey2pk(Mypubkey()); + if (AddNormalinputs(mtx, mypk, txfee + CC_MARKER_VALUE, 0x1000, pk.IsValid())) { + mtx.vout.push_back(MakeCC1vout(cp->evalcode, CC_MARKER_VALUE, mypk)); + return (FinalizeCCTxExt(pk.IsValid(), FINALIZECCTX_NO_CHANGE_WHEN_DUST, cp, mtx, mypk, txfee, EncodeOraclesOpRet('F', oracletxid, mypk, CC_MARKER_VALUE))); } - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "error adding normal inputs"); + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "error adding normal inputs"); } -UniValue OracleRegister(const CPubKey& pk, int64_t txfee,uint256 oracletxid,int64_t datafee) +UniValue OracleRegister(const CPubKey& pk, int64_t txfee, uint256 oracletxid, int64_t datafee) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,markerpubkey,batonpk,oraclespk; struct CCcontract_info *cp,C; char markeraddr[64],batonaddr[64]; - std::string name,desc,format; int32_t numvouts; uint256 hashBlock; CTransaction tx; + CPubKey mypk, markerpubkey, batonpk, oraclespk; + struct CCcontract_info *cp, C; + char markeraddr[64], batonaddr[64]; + std::string name, desc, format; + uint256 hashBlock; + CTransaction tx; - cp = CCinit(&C,EVAL_ORACLES); - if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_ORACLES]?0:CC_TXFEE; - if (myGetTransaction(oracletxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("oraclescc",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()); - if ( datafee < txfee ) - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "datafee must be txfee or more"); - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - oraclespk = GetUnspendable(cp,0); - batonpk = OracleBatonPk(batonaddr,cp); - markerpubkey = CCtxidaddr(markeraddr,oracletxid); - if (AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,4,pk.IsValid())) - { - 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)); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeOraclesOpRet('R',oracletxid,mypk,datafee))); + cp = CCinit(&C, EVAL_ORACLES); + if (txfee == 0) + txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_ORACLES] ? 0 : CC_TXFEE; + if (!myGetTransaction(oracletxid, tx, hashBlock) || tx.vout.size() <= 0) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); + if (DecodeOraclesCreateOpRet(tx.vout.back().scriptPubKey, name, desc, format) != 'C') + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); + if (datafee < txfee) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "datafee must be txfee or more"); + mypk = pk.IsValid() ? pk : pubkey2pk(Mypubkey()); + oraclespk = GetUnspendable(cp, 0); + batonpk = OracleBatonPk(batonaddr, cp); + markerpubkey = CCtxidaddr(markeraddr, oracletxid); + if (AddNormalinputs(mtx, mypk, txfee + 2 * CC_MARKER_VALUE, 0x1000, pk.IsValid())) { + 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)); + return (FinalizeCCTxExt(pk.IsValid(), FINALIZECCTX_NO_CHANGE_WHEN_DUST, cp, mtx, mypk, txfee, EncodeOraclesOpRet('R', oracletxid, mypk, datafee))); } - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "error adding normal inputs"); + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "error adding normal inputs"); } -UniValue OracleSubscribe(const CPubKey& pk, int64_t txfee,uint256 oracletxid,CPubKey publisher,int64_t amount) +UniValue OracleSubscribe(const CPubKey& pk, int64_t txfee, uint256 oracletxid, CPubKey publisher, int64_t amount) { - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction tx; - 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; - if (myGetTransaction(oracletxid,tx,hashBlock)==0 || (numvouts=tx.vout.size())<=0) - CCERR_RESULT("oraclescc",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()); - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - markerpubkey = CCtxidaddr(markeraddr,oracletxid); - if ( AddNormalinputs(mtx,mypk,amount + txfee + CC_MARKER_VALUE,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)); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeOraclesOpRet('S',oracletxid,mypk,amount))); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction tx; + CPubKey mypk, markerpubkey; + struct CCcontract_info *cp, C; + char markeraddr[64]; + std::string name, desc, format; + uint256 hashBlock; + cp = CCinit(&C, EVAL_ORACLES); + if (txfee == 0) + txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_ORACLES] ? 0 : CC_TXFEE; + if (!myGetTransaction(oracletxid, tx, hashBlock) || tx.vout.size() <= 0) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); + if (DecodeOraclesCreateOpRet(tx.vout.back().scriptPubKey, name, desc, format) != 'C') + CCERR_RESULT("oraclescc", 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, 0x1000, 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)); + return (FinalizeCCTxExt(pk.IsValid(), FINALIZECCTX_NO_CHANGE_WHEN_DUST, cp, mtx, mypk, txfee, EncodeOraclesOpRet('S', oracletxid, mypk, amount))); } - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "error adding normal inputs"); + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "error adding normal inputs"); } -UniValue OracleData(const CPubKey& pk, int64_t txfee,uint256 oracletxid,std::vector data) +UniValue OracleData(const CPubKey& pk, int64_t txfee, uint256 oracletxid, std::vector data) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CScript pubKey; CPubKey mypk,batonpk; int64_t offset,datafee,inputs,CCchange = 0; struct CCcontract_info *cp,C; uint256 batontxid,hashBlock; - char coinaddr[64],batonaddr[64]; std::vector prevdata; CTransaction tx; std::string name,description,format; int32_t len,numvouts; + CScript pubKey; + CPubKey mypk, batonpk; + int64_t offset, datafee, inputs, CCchange = 0; + struct CCcontract_info *cp, C; + uint256 batontxid, hashBlock; + char coinaddr[64], batonaddr[64]; + std::vector prevdata; + CTransaction tx; + std::string name, description, format; + int32_t len; - cp = CCinit(&C,EVAL_ORACLES); - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - if ( data.size() > 8192 ) - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "datasize " << (int32_t)data.size() << " is too big"); - if ( (datafee= OracleDatafee(pubKey,oracletxid,mypk)) <= 0 ) - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "datafee " << (double)datafee/COIN << "is illegal"); - if ( myGetTransaction(oracletxid,tx,hashBlock) != 0 && (numvouts=tx.vout.size()) > 0 ) + cp = CCinit(&C, EVAL_ORACLES); + mypk = pk.IsValid() ? pk : pubkey2pk(Mypubkey()); + if (data.size() > 8192) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "datasize " << (int32_t)data.size() << " is too big"); + if ((datafee = OracleDatafee(pubKey, oracletxid, mypk)) <= 0) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "datafee " << (double)datafee / COIN << "is illegal"); + if (myGetTransaction(oracletxid, tx, hashBlock) && tx.vout.size() > 0) { + if (DecodeOraclesCreateOpRet(tx.vout.back().scriptPubKey, name, description, format) == 'C') { + if (oracle_parse_data_format(data, format) == 0) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "data does not match length or content format specification"); + } else + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "invalid oracle txid opret data"); + } else + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "invalid oracle txid"); + if (txfee == 0) + txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_ORACLES] ? 0 : CC_TXFEE; + GetCCaddress(cp, coinaddr, mypk); + if (AddNormalinputs(mtx, mypk, txfee + CC_MARKER_VALUE, 0x1000, pk.IsValid()) > 0) // have enough funds even if baton utxo not there { - if ( DecodeOraclesCreateOpRet(tx.vout[numvouts-1].scriptPubKey,name,description,format) == 'C' ) - { - if (oracle_parse_data_format(data,format)==0) - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "data does not match length or content format specification"); - } + batonpk = OracleBatonPk(batonaddr, cp); + batontxid = OracleBatonUtxo(CC_MARKER_VALUE, 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 - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "invalid oracle txid opret data"); - } - else - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "invalid oracle txid"); - if ( txfee == 0 ) - txfee = ASSETCHAINS_CCZEROTXFEE[EVAL_ORACLES]?0:CC_TXFEE; - 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 - { - batonpk = OracleBatonPk(batonaddr,cp); - batontxid = OracleBatonUtxo(CC_MARKER_VALUE,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 > datafee ) + fprintf(stderr, "warning: couldnt find baton utxo %s\n", batonaddr); + if ((inputs = AddOracleInputs(cp, mtx, oracletxid, mypk, datafee, 0x1000)) >= datafee) { + if (inputs > datafee) CCchange = (inputs - datafee); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,batonpk)); - mtx.vout.push_back(CTxOut(datafee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeOraclesData('D',oracletxid,batontxid,mypk,data))); + mtx.vout.push_back(MakeCC1vout(cp->evalcode, CCchange, mypk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode, CC_MARKER_VALUE, batonpk)); + mtx.vout.push_back(CTxOut(datafee, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + return FinalizeCCTxExt(pk.IsValid(), FINALIZECCTX_NO_CHANGE_WHEN_DUST, cp, mtx, mypk, txfee, EncodeOraclesData('D', oracletxid, batontxid, mypk, data)); } else - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "couldnt find enough oracle inputs " << coinaddr << ", limit 1 per utxo"); + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "couldnt find enough oracle inputs " << coinaddr << ", limit 1 per utxo"); } - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "couldnt add normal inputs"); + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "couldnt add normal inputs"); } -UniValue OracleDataSample(uint256 reforacletxid,uint256 txid) +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; - 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 ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D' && reforacletxid == oracletxid ) - { - if ( (formatstr= (char *)format.c_str()) == 0 ) - formatstr = (char *)""; - result.push_back(Pair("txid",uint256_str(str,txid))); - result.push_back(Pair("data",OracleFormat((uint8_t *)data.data(),(int32_t)data.size(),formatstr,(int32_t)format.size()))); - return(result); - } - else error="invalid data tx"; - } - else error="cannot find data txid"; - } - else error="invalid oracles txid"; - } - else error="cannot find oracles txid"; - result.push_back(Pair("result","error")); - result.push_back(Pair("error",error)); - return(result); + UniValue result(UniValue::VOBJ); + CTransaction datatx, oracletx; + uint256 hashBlock, btxid, oracletxid; + std::string error; + struct CCcontract_info *cp, C; + CPubKey pk; + std::string name, description, format; + 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 && oracletx.vout.size() > 0) { + if (DecodeOraclesCreateOpRet(oracletx.vout.back().scriptPubKey, name, description, format) == 'C') { + if (FetchCCtx(txid, datatx, cp) && datatx.vout.size() > 0) { + if (DecodeOraclesData(datatx.vout.back().scriptPubKey, oracletxid, btxid, pk, data) == 'D' && reforacletxid == oracletxid) { + if ((formatstr = (char*)format.c_str()) == 0) + formatstr = (char*)""; + result.push_back(Pair("txid", uint256_str(str, txid))); + result.push_back(Pair("data", OracleFormat((uint8_t*)data.data(), (int32_t)data.size(), formatstr, (int32_t)format.size()))); + return (result); + } else + error = "invalid data tx"; + } else + error = "cannot find data txid"; + } else + error = "invalid oracles txid"; + } else + error = "cannot find oracles txid"; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", error)); + return (result); } -UniValue OracleDataSamples(uint256 reforacletxid,char* batonaddr,int32_t num) +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; + UniValue result(UniValue::VOBJ), b(UniValue::VARR); + CTransaction tx, oracletx; + uint256 txid, hashBlock, btxid, oracletxid; + CPubKey pk; + std::string name, description, format; + int32_t 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); - 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' ) - { + cp = CCinit(&C, EVAL_ORACLES); + result.push_back(Pair("result", "success")); + if (myGetTransaction(reforacletxid, oracletx, hashBlock) != 0 && oracletx.vout.size() > 0) { + if (DecodeOraclesCreateOpRet(oracletx.vout.back().scriptPubKey, name, description, format) == 'C') { 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++) - { - 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 ) - { - Getscriptaddress(addr,txmempool.vout[1].scriptPubKey); - if (strcmp(addr,batonaddr)!=0) continue; - if ( (formatstr= (char *)format.c_str()) == 0 ) - formatstr = (char *)""; + myGet_mempool_txs(tmp_txs, EVAL_ORACLES, 'D'); + for (std::vector::const_iterator it = tmp_txs.begin(); it != tmp_txs.end(); it++) { + const CTransaction& txmempool = *it; + const uint256& hash = txmempool.GetHash(); + if (txmempool.vout.size() >= 2 && ValidateCCtx(txmempool, cp) && txmempool.vout[1].nValue == CC_MARKER_VALUE && DecodeOraclesData(txmempool.vout.back().scriptPubKey, oracletxid, btxid, pk, data) == 'D' && reforacletxid == oracletxid) { + Getscriptaddress(addr, txmempool.vout[1].scriptPubKey); + if (strcmp(addr, batonaddr) != 0) + continue; + if ((formatstr = (char*)format.c_str()) == 0) + formatstr = (char*)""; UniValue a(UniValue::VOBJ); - a.push_back(Pair("txid",hash.GetHex())); - a.push_back(Pair("data",OracleFormat((uint8_t *)data.data(),(int32_t)data.size(),formatstr,(int32_t)format.size()))); + a.push_back(Pair("txid", hash.GetHex())); + a.push_back(Pair("data", OracleFormat((uint8_t*)data.data(), (int32_t)data.size(), formatstr, (int32_t)format.size()))); b.push_back(a); - if ( ++n >= num && num != 0) - { - result.push_back(Pair("samples",b)); - return(result); + if (++n >= num && num != 0) { + result.push_back(Pair("samples", b)); + return (result); } } } - SetCCtxids(txids,batonaddr,true,EVAL_ORACLES,CC_MARKER_VALUE,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 ( tx.vout[1].nValue==CC_MARKER_VALUE && DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D' && reforacletxid == oracletxid ) - { - if ( (formatstr= (char *)format.c_str()) == 0 ) - formatstr = (char *)""; + SetCCtxids(txids, batonaddr, true, EVAL_ORACLES, CC_MARKER_VALUE, 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) && tx.vout.size() >= 2) { + if (tx.vout[1].nValue == CC_MARKER_VALUE && DecodeOraclesData(tx.vout.back().scriptPubKey, oracletxid, btxid, pk, data) == 'D' && reforacletxid == oracletxid) { + if ((formatstr = (char*)format.c_str()) == NULL) + formatstr = (char*)""; UniValue a(UniValue::VOBJ); - a.push_back(Pair("txid",txid.GetHex())); - a.push_back(Pair("data",OracleFormat((uint8_t *)data.data(),(int32_t)data.size(),formatstr,(int32_t)format.size()))); + a.push_back(Pair("txid", txid.GetHex())); + a.push_back(Pair("data", OracleFormat((uint8_t*)data.data(), (int32_t)data.size(), formatstr, (int32_t)format.size()))); b.push_back(a); - if ( ++n >= num && num != 0) - { - result.push_back(Pair("samples",b)); - return(result); + if (++n >= num && num != 0) { + result.push_back(Pair("samples", b)); + return (result); } } } } } - } - else - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); - } - else - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); - result.push_back(Pair("samples",b)); - return(result); + } else + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); + } else + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); + result.push_back(Pair("samples", b)); + return (result); } UniValue OracleInfo(uint256 origtxid) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR); - std::vector > unspentOutputs; int32_t height; + UniValue result(UniValue::VOBJ), a(UniValue::VARR); + std::vector> unspentOutputs; + int32_t height; CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction tx; std::string name,description,format; uint256 hashBlock,txid,oracletxid,batontxid; CPubKey pk; - struct CCcontract_info *cp,C; int64_t datafee,funding; char str[67],markeraddr[64],numstr[64],batonaddr[64]; std::vector data; - std::map> publishers; + CTransaction tx; + std::string name, description, format; + uint256 hashBlock, txid, oracletxid, batontxid; + CPubKey pk; + struct CCcontract_info *cp, C; + int64_t datafee, funding; + char markeraddr[64], batonaddr[64]; + std::map> publishers; - 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 - { - if ( tx.vout.size() > 0 && DecodeOraclesCreateOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,name,description,format) == 'C' ) - { - result.push_back(Pair("result","success")); - result.push_back(Pair("txid",uint256_str(str,origtxid))); - result.push_back(Pair("name",name)); - result.push_back(Pair("description",description)); - result.push_back(Pair("format",format)); - result.push_back(Pair("marker",markeraddr)); - SetCCunspents(unspentOutputs,markeraddr,false); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { + cp = CCinit(&C, EVAL_ORACLES); + CCtxidaddr(markeraddr, origtxid); + if (!myGetTransaction(origtxid, tx, hashBlock)) + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex()); + else { + if (tx.vout.size() > 0 && DecodeOraclesCreateOpRet(tx.vout.back().scriptPubKey, name, description, format) == 'C') { + result.push_back(Pair("result", "success")); + result.push_back(Pair("txid", origtxid.GetHex())); + result.push_back(Pair("name", name)); + result.push_back(Pair("description", description)); + result.push_back(Pair("format", format)); + result.push_back(Pair("marker", markeraddr)); + SetCCunspents(unspentOutputs, markeraddr, false); + for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { txid = it->first.txhash; height = (int32_t)it->second.blockHeight; - if ( FetchCCtx(txid,tx,cp) && 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) - { - publishers[pk].first=txid; - publishers[pk].second=height; + if (FetchCCtx(txid, tx, cp) && tx.vout.size() > 0 && + DecodeOraclesOpRet(tx.vout.back().scriptPubKey, oracletxid, pk, datafee) == 'R' && oracletxid == origtxid) { + if (publishers.find(pk) == publishers.end() || height > publishers[pk].second) { + publishers[pk].first = txid; + publishers[pk].second = height; } } } - 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') - { + for (std::map>::iterator it = publishers.begin(); it != publishers.end(); ++it) { + if (FetchCCtx(it->second.first, tx, cp) && DecodeOraclesOpRet(tx.vout.back().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); - obj.push_back(Pair("baton",batonaddr)); - obj.push_back(Pair("batontxid",uint256_str(str,batontxid))); - funding = LifetimeOraclesFunds(cp,oracletxid,pk); - sprintf(numstr,"%.8f",(double)funding/COIN); - obj.push_back(Pair("lifetime",numstr)); - funding = AddOracleInputs(cp,mtx,oracletxid,pk,0,0); - sprintf(numstr,"%.8f",(double)funding/COIN); - obj.push_back(Pair("funds",numstr)); - sprintf(numstr,"%.8f",(double)datafee/COIN); - obj.push_back(Pair("datafee",numstr)); + std::vector data; + + obj.push_back(Pair("publisher", HexStr(pk))); + Getscriptaddress(batonaddr, tx.vout[1].scriptPubKey); + batontxid = OracleBatonUtxo(CC_MARKER_VALUE, cp, oracletxid, batonaddr, pk, data); + obj.push_back(Pair("baton", batonaddr)); + obj.push_back(Pair("batontxid", batontxid.GetHex())); + CAmount lifefunding = LifetimeOraclesFunds(cp, oracletxid, pk); + obj.push_back(Pair("lifetime", ValueFromAmount(lifefunding))); + CAmount funding = AddOracleInputs(cp, mtx, oracletxid, pk, 0, 0); + obj.push_back(Pair("funds", ValueFromAmount(funding))); + obj.push_back(Pair("datafee", ValueFromAmount(datafee))); a.push_back(obj); } } - result.push_back(Pair("registered",a)); - } - else - CCERR_RESULT("oraclescc",CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); + result.push_back(Pair("registered", a)); + } else + CCERR_RESULT("oraclescc", CCLOG_INFO, stream << "invalid oracletxid " << oracletxid.GetHex()); } - return(result); + return (result); } 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'); - for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) - { + UniValue result(UniValue::VARR); + std::vector txids; + struct CCcontract_info *cp, C; + uint256 txid, hashBlock; + CTransaction createtx; + std::string name, description, format; + + cp = CCinit(&C, EVAL_ORACLES); + SetCCtxids(txids, cp->normaladdr, false, cp->evalcode, CC_MARKER_VALUE, zeroid, 'C'); + for (std::vector::const_iterator it = txids.begin(); it != txids.end(); it++) { txid = *it; - if ( myGetTransaction(txid,createtx,hashBlock) != 0 ) - { - if ( createtx.vout.size() > 0 && DecodeOraclesCreateOpRet(createtx.vout[createtx.vout.size()-1].scriptPubKey,name,description,format) == 'C' ) - { - result.push_back(uint256_str(str,txid)); + if (myGetTransaction(txid, createtx, hashBlock) != 0) { + if (createtx.vout.size() > 0 && DecodeOraclesCreateOpRet(createtx.vout.back().scriptPubKey, name, description, format) == 'C') { + result.push_back(txid.GetHex()); } } } - return(result); + return (result); } diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 0c6edb284dc..d042edf0e04 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -1545,7 +1545,7 @@ UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) int32_t top=0,bottom=0,minimum=10000; std::vector> excludeScriptPubKeys; int8_t fixedAmount = 0; Paymentspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); - SetCCtxids(addressIndex,markeraddr,true); + SetAddressIndexOutputs(addressIndex,markeraddr,true); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { txid = it->first.txhash; diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index 6de6baa4000..118c59a92e4 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -19,6 +19,8 @@ #include "CCPegs.h" #include "../importcoin.h" #include "key_io.h" +#include "komodo_defs.h" + #include @@ -100,10 +102,7 @@ pegs CC is able to create a coin backed (by any supported coin with gateways CC #endif // PEGS_THRESHOLDS #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); -//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); diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 0fcc2e512c3..fc9c5294dca 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -274,7 +274,7 @@ static bool ValidateBetTx(struct CCcontract_info *cp, Eval *eval, const CTransac for (auto vout : bettx.vout) if (vout.scriptPubKey.IsPayToCryptoCondition()) ccOutputs += vout.nValue; - CAmount normalInputs = TotalPubkeyNormalInputs(bettx, pk); + CAmount normalInputs = TotalPubkeyNormalInputs(eval, bettx, pk); if (normalInputs < ccOutputs) { return eval->Invalid("bettx normal inputs not signed with pubkey in opret"); } @@ -2193,7 +2193,7 @@ UniValue PricesList(uint32_t filter, CPubKey mypk) }; - SetCCtxids(addressIndex, cp->normaladdr, false); // old normal marker + SetAddressIndexOutputs(addressIndex, cp->normaladdr, false); // old normal marker for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) { if( it->first.index == NVOUT_NORMALMARKER ) @@ -2201,7 +2201,7 @@ UniValue PricesList(uint32_t filter, CPubKey mypk) } /* for future when switch to cc marker only - SetCCtxids(addressIndexCC, cp->unspendableCCaddr, true); // cc marker + SetAddressIndexOutputs(addressIndexCC, cp->unspendableCCaddr, true); // cc marker for (std::vector >::const_iterator it = addressIndexCC.begin(); it != addressIndexCC.end(); it++) { priceslist(it, 1); @@ -2333,7 +2333,7 @@ void prices_getorderbook(std::map > & bookmatc cp = CCinit(&C, EVAL_PRICES); // add all bets: - SetCCtxids(addressIndex, cp->normaladdr, false); // old normal marker + SetAddressIndexOutputs(addressIndex, cp->normaladdr, false); // old normal marker for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) { if (it->first.index == NVOUT_NORMALMARKER) diff --git a/src/cc/priceslibs/Makefile b/src/cc/priceslibs/Makefile index 872a4a29337..9c0baa0a7be 100644 --- a/src/cc/priceslibs/Makefile +++ b/src/cc/priceslibs/Makefile @@ -16,4 +16,4 @@ libnistrandomparser.dll: NistRandomParser.cpp cjsonpointer.cpp cjsonpointer.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 + -rm libpricessampleparser.* libnistrandomparser.*. \ No newline at end of file diff --git a/src/cc/priceslibs/build-win.sh b/src/cc/priceslibs/build-win.sh new file mode 100755 index 00000000000..957212e8dfe --- /dev/null +++ b/src/cc/priceslibs/build-win.sh @@ -0,0 +1,11 @@ +#!/bin/bash +export HOST=x86_64-w64-mingw32 +CXX=x86_64-w64-mingw32-g++-posix +CC=x86_64-w64-mingw32-gcc-posix +PREFIX="$(pwd)/depends/$HOST" + +set -eu -o pipefail +set -x + +echo Making prices feeds custom libs... +CC="${CC} -g " CXX="${CXX} -g " make dll \ No newline at end of file diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index 1bc79ef6dde..3b498ff7185 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -297,7 +297,7 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t if ( !CheckTxFee(tx, txfee, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime, dummy) ) return eval->Invalid("txfee is too high"); amount = vinTx.vout[0].nValue; - reward = RewardsCalc((int64_t)amount,tx.vin[0].prevout.hash,(int64_t)APR,(int64_t)minseconds,(int64_t)maxseconds,GetLatestTimestamp(eval->GetCurrentHeight())); + reward = RewardsCalc((int64_t)amount,tx.vin[0].prevout.hash,(int64_t)APR,(int64_t)minseconds,(int64_t)maxseconds,GetLatestTimestamp(eval->GetCurrentHeightCompat())); if ( reward == 0 ) return eval->Invalid("no eligible rewards"); if ( numvins == 1 && tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) @@ -722,13 +722,13 @@ std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint2 fprintf(stderr,"inputs %.8f CCchange %.8f amount %.8f reward %.8f\n",(double)inputs/COIN,(double)CCchange/COIN,(double)amount/COIN,(double)reward/COIN); mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,rewardspk)); mtx.vout.push_back(CTxOut(amount+reward,scriptPubKey)); - return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid))); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid))); } else { firstmtx.vout.push_back(CTxOut(amount-txfee*2,scriptPubKey)); fprintf(stderr,"not enough rewards funds to payout %.8f, recover mode tx\n",(double)(reward+txfee)/COIN); - return(FinalizeCCTx(-1LL,cp,firstmtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid))); + return(FinalizeCCTx(0,cp,firstmtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid))); } } else diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index 9d101857e0b..bc46631e306 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -186,7 +186,7 @@ uint8_t rogue_highlanderopretdecode(uint256 &gametxid, uint256 &tokenid, int32_t else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRetV1(scriptPubKey, tokenid, voutPubkeys, opretsDummy)) != 0 ) { //fprintf(stderr,"decode opret %c tokenid.%s\n",script[1],tokenid.GetHex().c_str()); - GetTokenData(tokenid, tokenData, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx + GetTokenData(NULL, tokenid, tokenData, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx vopret = vopretNonfungible; } if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> gametxid; ss >> symbol; ss >> pname; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_ROGUE && (f == 'H' || f == 'Q') ) @@ -855,7 +855,7 @@ UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); - burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); + burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY_FIXED)); roguepk = GetUnspendable(cp,0); rogue_univalue(result,"register",-1,-1); playertxid = tokenid = zeroid; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index fcae87cb6c4..fb2b3399f66 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -182,12 +182,13 @@ class CMainParams : public CChainParams { assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); vFixedSeeds.clear(); vSeeds.clear(); - vSeeds.push_back(CDNSSeedData("komodoseeds.com", "kmd.komodoseeds.com")); // Static contolled seeds list (Kolo) - vSeeds.push_back(CDNSSeedData("komodoseeds.com", "dynamic.komodoseeds.com")); // Active seeds crawler (Kolo) + //vSeeds.push_back(CDNSSeedData("komodoseeds.com", "kmd.komodoseeds.com")); // Static contolled seeds list (Kolo) + //vSeeds.push_back(CDNSSeedData("komodoseeds.com", "dynamic.komodoseeds.com")); // Active seeds crawler (Kolo) // TODO: we need more seed crawlers from other community members base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,60); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,85); base58Prefixes[SECRET_KEY] = std::vector(1,188); + base58Prefixes[CRYPTOCONDITION_ADDRESS] = std::vector(1,0x1c); base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container >(); // guarantees the first two characters, when base58 encoded, are "zc" @@ -197,6 +198,8 @@ class CMainParams : public CChainParams { // guarantees the first two characters, when base58 encoded, are "SK" base58Prefixes[ZCSPENDING_KEY] = {171,54}; + base58Prefixes[CRYPTOCONDITION_ADDRESS] = std::vector(1,0x1c); + bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zs"; bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviews"; bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivks"; @@ -307,6 +310,8 @@ class CTestNetParams : public CChainParams { base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,0); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,5); base58Prefixes[SECRET_KEY] = std::vector(1,128); + base58Prefixes[CRYPTOCONDITION_ADDRESS] = std::vector(1,0x1c); + base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container >(); base58Prefixes[ZCPAYMENT_ADDRRESS] = {20,81}; @@ -428,6 +433,8 @@ class CRegTestParams : public CChainParams { base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,60); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,85); base58Prefixes[SECRET_KEY] = std::vector(1,188); + base58Prefixes[CRYPTOCONDITION_ADDRESS] = std::vector(1,0x1c); + //base58Prefixes[PUBKEY_ADDRESS] = {0x1D,0x25}; //base58Prefixes[SCRIPT_ADDRESS] = {0x1C,0xBA}; //base58Prefixes[SECRET_KEY] = {0xEF}; diff --git a/src/chainparams.h b/src/chainparams.h index daa16af8c40..23916b7ef17 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -63,6 +63,7 @@ class CChainParams ZCPAYMENT_ADDRRESS, ZCSPENDING_KEY, ZCVIEWING_KEY, + CRYPTOCONDITION_ADDRESS, MAX_BASE58_TYPES }; diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index e3dac3ca641..4a6a2248632 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -23,12 +23,15 @@ * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly. */ static SeedSpec6 pnSeed6_main[] = { - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0xec}, 27485}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0xec}, 27487}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x69,0x6f}, 27485}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x69,0x6f}, 27487}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0x48}, 27485}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0x48}, 27487} +/** + * no need in zcash seeds: + * {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0xec}, 27485}, + * {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0xec}, 27487}, + * {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x69,0x6f}, 27485}, + * {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x69,0x6f}, 27487}, + * {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0x48}, 27485}, + * {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0x48}, 27487} + */ }; static SeedSpec6 pnSeed6_test[] = { diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 084bbd5ceed..36bffea8cce 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2022 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -35,7 +35,8 @@ * for both bitcoind and bitcoin-core, to make it harder for attackers to * target servers or GUI users specifically. */ -const std::string CLIENT_NAME = GetArg("-clientname", "MagicBean"); + +const std::string CLIENT_NAME = GetArg("-clientname", "komodod"); /** * Client version number @@ -117,16 +118,10 @@ const std::string CLIENT_NAME = GetArg("-clientname", "MagicBean"); const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX); const std::string CLIENT_DATE(BUILD_DATE); -std::string FormatVersion(int nVersion) +std::string FormatVersion(int nVersion, const std::string &stage) { - if (nVersion % 100 < 25) - return strprintf("%d.%d.%d-beta%d", nVersion / 1000000, (nVersion / 10000) % 100, (nVersion / 100) % 100, (nVersion % 100)+1); - if (nVersion % 100 < 50) - return strprintf("%d.%d.%d-rc%d", nVersion / 1000000, (nVersion / 10000) % 100, (nVersion / 100) % 100, (nVersion % 100)-24); - else if (nVersion % 100 == 50) - return strprintf("%d.%d.%d", nVersion / 1000000, (nVersion / 10000) % 100, (nVersion / 100) % 100); - else - return strprintf("%d.%d.%d-%d", nVersion / 1000000, (nVersion / 10000) % 100, (nVersion / 100) % 100, (nVersion % 100)-50); + // use own rules to determine the development stage + return strprintf("%d.%d.%d%s", nVersion / 1000000, (nVersion / 10000) % 100, (nVersion / 100) % 100, stage); } std::string FormatFullVersion() @@ -136,6 +131,7 @@ std::string FormatFullVersion() /** * Format the subversion field according to BIP 14 spec (https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki) + * added derived repo extension's version */ std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector& comments) { diff --git a/src/clientversion.h b/src/clientversion.h index eced952e12f..f05f31d3507 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2022 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -30,8 +30,8 @@ */ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it -#define CLIENT_VERSION_MAJOR 3 -#define CLIENT_VERSION_MINOR 0 +#define CLIENT_VERSION_MAJOR 0 +#define CLIENT_VERSION_MINOR 6 #define CLIENT_VERSION_REVISION 1 #define CLIENT_VERSION_BUILD 0 @@ -78,7 +78,7 @@ extern const std::string CLIENT_BUILD; extern const std::string CLIENT_DATE; -std::string FormatVersion(int nVersion); +std::string FormatVersion(int nVersion, const std::string &stage = std::string()); std::string FormatFullVersion(); std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector& comments); diff --git a/src/coins.cpp b/src/coins.cpp index 92206b65379..1c404ef5954 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -565,9 +565,6 @@ const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const return coins->vout[input.prevout.n]; } -//uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); -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); -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; const CScript &CCoinsViewCache::GetSpendFor(const CCoins *coins, const CTxIn& input) { @@ -599,14 +596,14 @@ CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTr if ( interestp != 0 ) *interestp = 0; if ( tx.IsCoinImport() ) - return GetCoinImportValue(tx); + return GetCoinImportValue(tx, tiptime, nHeight); if ( tx.IsCoinBase() != 0 ) return 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { if (tx.IsPegsImport() && i==0) { - nResult = GetCoinImportValue(tx); + nResult = GetCoinImportValue(tx, tiptime, nHeight); continue; } value = GetOutputFor(tx.vin[i]).nValue; diff --git a/src/consensus/upgrades.cpp b/src/consensus/upgrades.cpp index b75e0c002c1..3a0deff5bf4 100644 --- a/src/consensus/upgrades.cpp +++ b/src/consensus/upgrades.cpp @@ -18,17 +18,11 @@ ******************************************************************************/ #include "consensus/upgrades.h" +#include "komodo_nSPV_defs.h" + extern int32_t KOMODO_NSPV; #define NSPV_BRANCHID 0x76b809bb -#ifndef KOMODO_NSPV_FULLNODE -#define KOMODO_NSPV_FULLNODE (KOMODO_NSPV <= 0) -#endif // !KOMODO_NSPV_FULLNODE - -#ifndef KOMODO_NSPV_SUPERLITE -#define KOMODO_NSPV_SUPERLITE (KOMODO_NSPV > 0) -#endif // !KOMODO_NSPV_SUPERLITE - /** * General information about each network upgrade. * Ordered by Consensus::UpgradeIndex. diff --git a/src/crosschain.cpp b/src/crosschain.cpp index 0a081c0868a..375d5ac9174 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -43,8 +43,6 @@ int NOTARISATION_SCAN_LIMIT_BLOCKS = 1440; -CBlockIndex *komodo_getblockindex(uint256 hash); - /* On KMD */ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight, diff --git a/src/crypto/equihash.h b/src/crypto/equihash.h index 57c434dae96..7d1275079ce 100644 --- a/src/crypto/equihash.h +++ b/src/crypto/equihash.h @@ -13,6 +13,7 @@ #include "komodo_nk.h" #include +#include #include #include #include diff --git a/src/cryptoconditions/Makefile.am b/src/cryptoconditions/Makefile.am index 263cb4f8d53..5565ffa27bb 100644 --- a/src/cryptoconditions/Makefile.am +++ b/src/cryptoconditions/Makefile.am @@ -1,30 +1,27 @@ -lib_LTLIBRARIES=libcryptoconditions.la -noinst_LTLIBRARIES=$(CRYPTOCONDITIONS_CORE) +noinst_LIBRARIES=libcryptoconditions_core.a SUBDIRS = src/include/secp256k1 -include_HEADERS = include/cryptoconditions.h +# optimisation off to allow better gdb/lldb +# CFLAGS = -g -O0 -# Have a separate build target for cryptoconditions that does not contain secp256k1 +# use to debug: +# CPPFLAGS = -DEMIT_ASN_DEBUG=1 -libcryptoconditions_la_SOURCES = include/cryptoconditions.h -libcryptoconditions_la_LIBADD = $(CRYPTOCONDITIONS_CORE) $(LIBSECP256K1) +include_HEADERS = include/cryptoconditions.h src/internal.h -AM_CFLAGS = -I$(top_srcdir)/src/asn -I$(top_srcdir)/include -I$(top_srcdir)/src/include \ - -Wall -Wno-pointer-sign -Wno-discarded-qualifiers +# Have a separate build target for cryptoconditions that does not contain secp256k1 LIBSECP256K1=src/include/secp256k1/libsecp256k1.la $(LIBSECP256K1): $(wildcard src/secp256k1/*) - $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) -march:x86-64 -g + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) -march:x86-64 -g -CRYPTOCONDITIONS_CORE=libcryptoconditions_core.la - -libcryptoconditions_core_la_SOURCES = \ - include/cryptoconditions.h \ +libcryptoconditions_core_a_SOURCES = \ src/cryptoconditions.c \ src/utils.c \ src/include/cJSON.c \ src/include/sha256.c \ + src/include/ripemd160.c \ src/include/ed25519/src/keypair.c \ src/include/ed25519/src/seed.c \ src/include/ed25519/src/verify.c \ @@ -51,7 +48,9 @@ libcryptoconditions_core_la_SOURCES = \ src/asn/Ed25519FingerprintContents.c \ src/asn/EvalFulfillment.c \ src/asn/Secp256k1FingerprintContents.c \ + src/asn/Secp256k1hashFingerprintContents.c \ src/asn/Secp256k1Fulfillment.c \ + src/asn/Secp256k1hashFulfillment.c \ src/asn/INTEGER.c \ src/asn/NativeEnumerated.c \ src/asn/NativeInteger.c \ @@ -75,18 +74,21 @@ libcryptoconditions_core_la_SOURCES = \ src/asn/per_decoder.c \ src/asn/per_encoder.c \ src/asn/per_opentype.c +libcryptoconditions_core_a_CPPFLAGS=-I. -I./src/include -I./src/asn test: bash -c '[ -d .env ] || virtualenv .env -p python3' .env/bin/pip install pytest + gdb -batch -ex run -ex bt --args .env/bin/python -m pytest -s -x -v 2>&1 | grep -v ^"No stack."$ test-debug-interactive: gdb -ex run --args python3 -m pytest -s -x -v asn: - cd src/asn; \ - mv asn_system.h asn_system.bak; \ - rm *.c *.h; \ - asn1c CryptoConditions.asn; \ - mv asn_system.bak asn_system.h +# run asn1c in a dedicated directory, use exactly the same asn1c version, when asn1c is done copy generated files manually into asn subdir +# cd src/asn; \ +# mv asn_system.h asn_system.bak; \ +# rm *.c *.h; \ +# asn1c CryptoConditions.asn; \ +# mv asn_system.bak asn_system.h diff --git a/src/cryptoconditions/include/cryptoconditions.h b/src/cryptoconditions/include/cryptoconditions.h index ebfeb5c1281..5f0865cb4e9 100644 --- a/src/cryptoconditions/include/cryptoconditions.h +++ b/src/cryptoconditions/include/cryptoconditions.h @@ -37,6 +37,7 @@ enum CCTypeId { CC_Threshold = 2, CC_Ed25519 = 4, CC_Secp256k1 = 5, + CC_Secp256k1hash = 6, CC_Eval = 15 }; @@ -55,7 +56,7 @@ typedef struct CC { struct CCType *type; union { // public key types - struct { uint8_t *publicKey, *signature; }; + struct { uint8_t *publicKey, *signature; uint8_t *publicKeyHash; }; // preimage struct { uint8_t *preimage; size_t preimageLength; }; // threshold @@ -64,11 +65,12 @@ typedef struct CC { struct { uint8_t *prefix; size_t prefixLength; struct CC *subcondition; size_t maxMessageLength; }; // eval - struct { uint8_t *code; size_t codeLength; }; + struct { uint8_t *code; size_t codeLength; /*uint8_t *param; size_t paramLength; int includeParamInFP;*/ }; // enable param when new generic evals added // anon struct { uint8_t fingerprint[32]; uint32_t subtypes; unsigned long cost; struct CCType *conditionType; }; }; + int dontFulfill; } CC; /* @@ -94,7 +96,9 @@ int cc_visit(CC *cond, struct CCVisitor visitor); int cc_signTreeEd25519(CC *cond, const uint8_t *privateKey, const uint8_t *msg, const size_t msgLength); int cc_signTreeSecp256k1Msg32(CC *cond, const uint8_t *privateKey, const uint8_t *msg32); +int cc_signTreeSecp256k1HashMsg32(CC *cond, const unsigned char *privateKey, const unsigned char *msg32); int cc_secp256k1VerifyTreeMsg32(const CC *cond, const uint8_t *msg32); +int cc_secp256k1HashVerifyTreeMsg32(const CC *cond, const unsigned char *msg32); size_t cc_conditionBinary(const CC *cond, uint8_t *buf); size_t cc_fulfillmentBinary(const CC *cond, uint8_t *buf, size_t bufLength); size_t cc_fulfillmentBinaryMixedMode(const CC *cond, uint8_t *buf, size_t bufLength); @@ -116,7 +120,8 @@ uint32_t cc_typeMask(const CC *cond); int cc_isAnon(const CC *cond); struct CC* cc_anon(const CC *cond); void cc_free(struct CC *cond); -struct CC* cc_copy(CC *cond); +struct CC* cc_copy(const CC *cond); +int cc_hasSubtypes(enum CCTypeId cctypeid); #ifdef __cplusplus } diff --git a/src/cryptoconditions/src/anon.c b/src/cryptoconditions/src/anon.c index dabcdcb20d2..b6b4c15dec1 100644 --- a/src/cryptoconditions/src/anon.c +++ b/src/cryptoconditions/src/anon.c @@ -36,7 +36,7 @@ CC* cc_anon(const CC *cond) { return out; } - +// assumes the root condition is a threshold CC *mkAnon(const Condition_t *asnCond) { CCType *realType = getTypeByAsnEnum(asnCond->present); @@ -46,7 +46,7 @@ CC *mkAnon(const Condition_t *asnCond) { } CC *cond = cc_new(CC_Anon); cond->conditionType = realType; - const CompoundSha256Condition_t *deets = &asnCond->choice.thresholdSha256; + const CompoundSha256Condition_t *deets = &asnCond->choice.thresholdSha256; // assumes CompoundSha256Condition_t and SimpleSha256Condition_t are same for fingerprint and cost memcpy(cond->fingerprint, deets->fingerprint.buf, 32); cond->cost = deets->cost; if (realType->getSubtypes) { @@ -59,7 +59,7 @@ static CC* anonFromJSON(const cJSON *params, char *err) { size_t len; cJSON *fingerprint_item = cJSON_GetObjectItem(params, "fingerprint"); if (!cJSON_IsString(fingerprint_item)) { - strcpy(err, "fingerprint must be a number"); + strcpy(err, "fingerprint must be a string"); return NULL; } unsigned char *fing = base64_decode(fingerprint_item->valuestring, &len); @@ -73,15 +73,34 @@ static CC* anonFromJSON(const cJSON *params, char *err) { strcpy(err, "cost must be a number"); return NULL; } - cJSON *subtypes_item = cJSON_GetObjectItem(params, "subtypes"); - if (!cJSON_IsNumber(subtypes_item)) { - strcpy(err, "subtypes must be a number"); + + /* it was not good to use asnType as it is just an internal utility var: + cJSON *asnType_item = cJSON_GetObjectItem(params, "asnType"); */ + cJSON *condType_item = cJSON_GetObjectItem(params, "cond_type"); + if (!cJSON_IsNumber(condType_item)) { + strcpy(err, "cond_type must be a number"); + return NULL; + } + if (condType_item->valueint < 0 || condType_item->valueint >= CCTypeRegistryLength) { + strcpy(err, "cond_type invalid"); return NULL; } + CCType * realType = CCTypeRegistry[condType_item->valueint]; + CC* cond=cc_new(CC_Anon); memcpy(cond->fingerprint,fing,len); cond->cost = (long) cost_item->valuedouble; - cond->subtypes = subtypes_item->valueint; + cond->subtypes = 0; + cond->conditionType = realType; + + if (cc_hasSubtypes(realType->typeId)) { + cJSON *subtypes_item = cJSON_GetObjectItem(params, "subtypes"); + if (!cJSON_IsNumber(subtypes_item)) { + strcpy(err, "subtypes must be a number"); + return NULL; + } + cond->subtypes = subtypes_item->valueint; + } return cond; } @@ -90,7 +109,15 @@ static void anonToJSON(const CC *cond, cJSON *params) { cJSON_AddItemToObject(params, "fingerprint", cJSON_CreateString(b64)); free(b64); cJSON_AddItemToObject(params, "cost", cJSON_CreateNumber(cond->cost)); - cJSON_AddItemToObject(params, "subtypes", cJSON_CreateNumber(cond->subtypes)); + if (cc_hasSubtypes(cc_typeId(cond))) + cJSON_AddItemToObject(params, "subtypes", cJSON_CreateNumber(cond->subtypes)); + + // cJSON_AddItemToObject(params, "asnType", cJSON_CreateNumber((int)cond->conditionType->asnType)); + // this was not good as asnType is not stored into any cc asn1 encoding, + // it is just an internal variable for indexing in the asn_MBR_Condition_1 array: + // better to use a persistent conditionType, + // the same field is used in the rust cryptocondition lib: + cJSON_AddItemToObject(params, "cond_type", cJSON_CreateNumber((int) cc_typeId(cond))); } diff --git a/src/cryptoconditions/src/asn/Condition.c b/src/cryptoconditions/src/asn/Condition.c index 49ec83e3b5a..7def6dc3aba 100644 --- a/src/cryptoconditions/src/asn/Condition.c +++ b/src/cryptoconditions/src/asn/Condition.c @@ -1,7 +1,7 @@ /* * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) * From ASN.1 module "Crypto-Conditions" - * found in "CryptoConditions.asn" + * found in "./CryptoConditions-pkhash.asn" */ #include "Condition.h" @@ -61,6 +61,15 @@ static asn_TYPE_member_t asn_MBR_Condition_1[] = { 0, "secp256k1Sha256" }, + { ATF_NOFLAGS, 0, offsetof(struct Condition, choice.secp256k1hashSha256), + (ASN_TAG_CLASS_CONTEXT | (6 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_SimpleSha256Condition, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "secp256k1hashSha256" + }, { ATF_NOFLAGS, 0, offsetof(struct Condition, choice.evalSha256), (ASN_TAG_CLASS_CONTEXT | (15 << 2)), -1, /* IMPLICIT tag at current level */ @@ -78,7 +87,8 @@ static const asn_TYPE_tag2member_t asn_MAP_Condition_tag2el_1[] = { { (ASN_TAG_CLASS_CONTEXT | (3 << 2)), 3, 0, 0 }, /* rsaSha256 */ { (ASN_TAG_CLASS_CONTEXT | (4 << 2)), 4, 0, 0 }, /* ed25519Sha256 */ { (ASN_TAG_CLASS_CONTEXT | (5 << 2)), 5, 0, 0 }, /* secp256k1Sha256 */ - { (ASN_TAG_CLASS_CONTEXT | (15 << 2)), 6, 0, 0 } /* evalSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (6 << 2)), 6, 0, 0 }, /* secp256k1hashSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (15 << 2)), 7, 0, 0 } /* evalSha256 */ }; static asn_CHOICE_specifics_t asn_SPC_Condition_specs_1 = { sizeof(struct Condition), @@ -86,7 +96,7 @@ static asn_CHOICE_specifics_t asn_SPC_Condition_specs_1 = { offsetof(struct Condition, present), sizeof(((struct Condition *)0)->present), asn_MAP_Condition_tag2el_1, - 7, /* Count of tags in the map */ + 8, /* Count of tags in the map */ 0, -1 /* Extensions start */ }; @@ -108,7 +118,7 @@ asn_TYPE_descriptor_t asn_DEF_Condition = { 0, /* No tags (count) */ 0, /* No PER visible constraints */ asn_MBR_Condition_1, - 7, /* Elements count */ + 8, /* Elements count */ &asn_SPC_Condition_specs_1 /* Additional specs */ }; diff --git a/src/cryptoconditions/src/asn/Condition.h b/src/cryptoconditions/src/asn/Condition.h index 2396c01523e..8a9e76cd948 100644 --- a/src/cryptoconditions/src/asn/Condition.h +++ b/src/cryptoconditions/src/asn/Condition.h @@ -1,19 +1,19 @@ /* * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) * From ASN.1 module "Crypto-Conditions" - * found in "CryptoConditions.asn" + * found in "./CryptoConditions-pkhash.asn" */ #ifndef _Condition_H_ #define _Condition_H_ -#include "asn_application.h" +#include /* Including external dependencies */ #include "SimpleSha256Condition.h" #include "CompoundSha256Condition.h" -#include "constr_CHOICE.h" +#include #ifdef __cplusplus extern "C" { @@ -28,6 +28,7 @@ typedef enum Condition_PR { Condition_PR_rsaSha256, Condition_PR_ed25519Sha256, Condition_PR_secp256k1Sha256, + Condition_PR_secp256k1hashSha256, Condition_PR_evalSha256 } Condition_PR; @@ -41,6 +42,7 @@ typedef struct Condition { SimpleSha256Condition_t rsaSha256; SimpleSha256Condition_t ed25519Sha256; SimpleSha256Condition_t secp256k1Sha256; + SimpleSha256Condition_t secp256k1hashSha256; SimpleSha256Condition_t evalSha256; } choice; @@ -56,4 +58,4 @@ extern asn_TYPE_descriptor_t asn_DEF_Condition; #endif #endif /* _Condition_H_ */ -#include "asn_internal.h" +#include diff --git a/src/cryptoconditions/src/asn/ConditionTypes.c b/src/cryptoconditions/src/asn/ConditionTypes.c index 16ca9d19c4f..dbcded60e94 100644 --- a/src/cryptoconditions/src/asn/ConditionTypes.c +++ b/src/cryptoconditions/src/asn/ConditionTypes.c @@ -1,7 +1,7 @@ /* * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) * From ASN.1 module "Crypto-Conditions" - * found in "CryptoConditions.asn" + * found in "./CryptoConditions-pkhash.asn" */ #include "ConditionTypes.h" diff --git a/src/cryptoconditions/src/asn/ConditionTypes.h b/src/cryptoconditions/src/asn/ConditionTypes.h index 595dd0aad60..61481af9423 100644 --- a/src/cryptoconditions/src/asn/ConditionTypes.h +++ b/src/cryptoconditions/src/asn/ConditionTypes.h @@ -1,17 +1,17 @@ /* * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) * From ASN.1 module "Crypto-Conditions" - * found in "CryptoConditions.asn" + * found in "./CryptoConditions-pkhash.asn" */ #ifndef _ConditionTypes_H_ #define _ConditionTypes_H_ -#include "asn_application.h" +#include /* Including external dependencies */ -#include "BIT_STRING.h" +#include #ifdef __cplusplus extern "C" { @@ -25,6 +25,7 @@ typedef enum ConditionTypes { ConditionTypes_rsaSha256 = 3, ConditionTypes_ed25519Sha256 = 4, ConditionTypes_secp256k1Sha256 = 5, + ConditionTypes_secp256k1hashSha256 = 6, ConditionTypes_evalSha256 = 15 } e_ConditionTypes; @@ -46,4 +47,4 @@ xer_type_encoder_f ConditionTypes_encode_xer; #endif #endif /* _ConditionTypes_H_ */ -#include "asn_internal.h" +#include diff --git a/src/cryptoconditions/src/asn/CryptoConditions.asn b/src/cryptoconditions/src/asn/CryptoConditions.asn index 42a3c88f174..94bdaa5857b 100644 --- a/src/cryptoconditions/src/asn/CryptoConditions.asn +++ b/src/cryptoconditions/src/asn/CryptoConditions.asn @@ -11,6 +11,7 @@ Crypto-Conditions DEFINITIONS AUTOMATIC TAGS ::= BEGIN rsaSha256 [3] SimpleSha256Condition, ed25519Sha256 [4] SimpleSha256Condition, secp256k1Sha256 [5] SimpleSha256Condition, + secp256k1hashSha256 [6] SimpleSha256Condition, evalSha256 [15] SimpleSha256Condition } @@ -32,6 +33,7 @@ Crypto-Conditions DEFINITIONS AUTOMATIC TAGS ::= BEGIN rsaSha256 (3), ed25519Sha256 (4), secp256k1Sha256 (5), + secp256k1hashSha256 (6), evalSha256 (15) } @@ -44,6 +46,7 @@ Crypto-Conditions DEFINITIONS AUTOMATIC TAGS ::= BEGIN rsaSha256 [3] RsaSha256Fulfillment, ed25519Sha256 [4] Ed25519Sha512Fulfillment, secp256k1Sha256 [5] Secp256k1Fulfillment, + secp256k1hashSha256 [6] Secp256k1hashFulfillment, evalSha256 [15] EvalFulfillment } @@ -77,8 +80,14 @@ Crypto-Conditions DEFINITIONS AUTOMATIC TAGS ::= BEGIN signature OCTET STRING (SIZE(64)) } + Secp256k1hashFulfillment ::= SEQUENCE { + publicKey OCTET STRING (SIZE(33)), + signature OCTET STRING (SIZE(64)) + } + EvalFulfillment ::= SEQUENCE { - code OCTET STRING + code OCTET STRING, + param OCTET STRING OPTIONAL } -- Fingerprint Content @@ -110,4 +119,8 @@ Crypto-Conditions DEFINITIONS AUTOMATIC TAGS ::= BEGIN publicKey OCTET STRING (SIZE(33)) } + Secp256k1hashFingerprintContents ::= SEQUENCE { + publicKeyHash OCTET STRING (SIZE(20)) + } + END diff --git a/src/cryptoconditions/src/asn/EvalFulfillment.c b/src/cryptoconditions/src/asn/EvalFulfillment.c index f43b21e1fd5..b642600dba8 100644 --- a/src/cryptoconditions/src/asn/EvalFulfillment.c +++ b/src/cryptoconditions/src/asn/EvalFulfillment.c @@ -16,18 +16,28 @@ static asn_TYPE_member_t asn_MBR_EvalFulfillment_1[] = { 0, "code" }, + { ATF_POINTER, 1, offsetof(struct EvalFulfillment, param), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "param" + }, }; static const ber_tlv_tag_t asn_DEF_EvalFulfillment_tags_1[] = { (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) }; static const asn_TYPE_tag2member_t asn_MAP_EvalFulfillment_tag2el_1[] = { - { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 } /* code */ + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* code */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 } /* param */ }; static asn_SEQUENCE_specifics_t asn_SPC_EvalFulfillment_specs_1 = { sizeof(struct EvalFulfillment), offsetof(struct EvalFulfillment, _asn_ctx), asn_MAP_EvalFulfillment_tag2el_1, - 1, /* Count of tags in the map */ + 2, /* Count of tags in the map */ 0, 0, 0, /* Optional elements (not needed) */ -1, /* Start extensions */ -1 /* Stop extensions */ @@ -52,7 +62,7 @@ asn_TYPE_descriptor_t asn_DEF_EvalFulfillment = { /sizeof(asn_DEF_EvalFulfillment_tags_1[0]), /* 1 */ 0, /* No PER visible constraints */ asn_MBR_EvalFulfillment_1, - 1, /* Elements count */ + 2, /* Elements count */ &asn_SPC_EvalFulfillment_specs_1 /* Additional specs */ }; diff --git a/src/cryptoconditions/src/asn/EvalFulfillment.h b/src/cryptoconditions/src/asn/EvalFulfillment.h index 3a35ac34162..922af1318b0 100644 --- a/src/cryptoconditions/src/asn/EvalFulfillment.h +++ b/src/cryptoconditions/src/asn/EvalFulfillment.h @@ -8,11 +8,11 @@ #define _EvalFulfillment_H_ -#include "asn_application.h" +#include /* Including external dependencies */ -#include "OCTET_STRING.h" -#include "constr_SEQUENCE.h" +#include +#include #ifdef __cplusplus extern "C" { @@ -21,6 +21,7 @@ extern "C" { /* EvalFulfillment */ typedef struct EvalFulfillment { OCTET_STRING_t code; + OCTET_STRING_t *param /* OPTIONAL */; /* Context for parsing across buffer boundaries */ asn_struct_ctx_t _asn_ctx; @@ -34,4 +35,4 @@ extern asn_TYPE_descriptor_t asn_DEF_EvalFulfillment; #endif #endif /* _EvalFulfillment_H_ */ -#include "asn_internal.h" +#include diff --git a/src/cryptoconditions/src/asn/Fulfillment.c b/src/cryptoconditions/src/asn/Fulfillment.c index faf43b7723c..ab44cb1f944 100644 --- a/src/cryptoconditions/src/asn/Fulfillment.c +++ b/src/cryptoconditions/src/asn/Fulfillment.c @@ -1,7 +1,7 @@ /* * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) * From ASN.1 module "Crypto-Conditions" - * found in "CryptoConditions.asn" + * found in "./CryptoConditions-pkhash.asn" */ #include "Fulfillment.h" @@ -61,6 +61,15 @@ static asn_TYPE_member_t asn_MBR_Fulfillment_1[] = { 0, "secp256k1Sha256" }, + { ATF_NOFLAGS, 0, offsetof(struct Fulfillment, choice.secp256k1hashSha256), + (ASN_TAG_CLASS_CONTEXT | (6 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_Secp256k1hashFulfillment, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "secp256k1hashSha256" + }, { ATF_NOFLAGS, 0, offsetof(struct Fulfillment, choice.evalSha256), (ASN_TAG_CLASS_CONTEXT | (15 << 2)), -1, /* IMPLICIT tag at current level */ @@ -78,7 +87,8 @@ static const asn_TYPE_tag2member_t asn_MAP_Fulfillment_tag2el_1[] = { { (ASN_TAG_CLASS_CONTEXT | (3 << 2)), 3, 0, 0 }, /* rsaSha256 */ { (ASN_TAG_CLASS_CONTEXT | (4 << 2)), 4, 0, 0 }, /* ed25519Sha256 */ { (ASN_TAG_CLASS_CONTEXT | (5 << 2)), 5, 0, 0 }, /* secp256k1Sha256 */ - { (ASN_TAG_CLASS_CONTEXT | (15 << 2)), 6, 0, 0 } /* evalSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (6 << 2)), 6, 0, 0 }, /* secp256k1hashSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (15 << 2)), 7, 0, 0 } /* evalSha256 */ }; static asn_CHOICE_specifics_t asn_SPC_Fulfillment_specs_1 = { sizeof(struct Fulfillment), @@ -86,7 +96,7 @@ static asn_CHOICE_specifics_t asn_SPC_Fulfillment_specs_1 = { offsetof(struct Fulfillment, present), sizeof(((struct Fulfillment *)0)->present), asn_MAP_Fulfillment_tag2el_1, - 7, /* Count of tags in the map */ + 8, /* Count of tags in the map */ 0, -1 /* Extensions start */ }; @@ -108,7 +118,7 @@ asn_TYPE_descriptor_t asn_DEF_Fulfillment = { 0, /* No tags (count) */ 0, /* No PER visible constraints */ asn_MBR_Fulfillment_1, - 7, /* Elements count */ + 8, /* Elements count */ &asn_SPC_Fulfillment_specs_1 /* Additional specs */ }; diff --git a/src/cryptoconditions/src/asn/Fulfillment.h b/src/cryptoconditions/src/asn/Fulfillment.h index 3d4ba518424..dbf19fcb0f1 100644 --- a/src/cryptoconditions/src/asn/Fulfillment.h +++ b/src/cryptoconditions/src/asn/Fulfillment.h @@ -1,22 +1,23 @@ /* * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) * From ASN.1 module "Crypto-Conditions" - * found in "CryptoConditions.asn" + * found in "./CryptoConditions-pkhash.asn" */ #ifndef _Fulfillment_H_ #define _Fulfillment_H_ -#include "asn_application.h" +#include /* Including external dependencies */ #include "PreimageFulfillment.h" #include "RsaSha256Fulfillment.h" #include "Ed25519Sha512Fulfillment.h" #include "Secp256k1Fulfillment.h" +#include "Secp256k1hashFulfillment.h" #include "EvalFulfillment.h" -#include "constr_CHOICE.h" +#include #ifdef __cplusplus extern "C" { @@ -31,6 +32,7 @@ typedef enum Fulfillment_PR { Fulfillment_PR_rsaSha256, Fulfillment_PR_ed25519Sha256, Fulfillment_PR_secp256k1Sha256, + Fulfillment_PR_secp256k1hashSha256, Fulfillment_PR_evalSha256 } Fulfillment_PR; @@ -48,6 +50,7 @@ typedef struct Fulfillment { RsaSha256Fulfillment_t rsaSha256; Ed25519Sha512Fulfillment_t ed25519Sha256; Secp256k1Fulfillment_t secp256k1Sha256; + Secp256k1hashFulfillment_t secp256k1hashSha256; EvalFulfillment_t evalSha256; } choice; @@ -67,4 +70,4 @@ extern asn_TYPE_descriptor_t asn_DEF_Fulfillment; #include "ThresholdFulfillment.h" #endif /* _Fulfillment_H_ */ -#include "asn_internal.h" +#include diff --git a/src/cryptoconditions/src/asn/Secp256k1hashFingerprintContents.c b/src/cryptoconditions/src/asn/Secp256k1hashFingerprintContents.c new file mode 100644 index 00000000000..3d4ccfd6e33 --- /dev/null +++ b/src/cryptoconditions/src/asn/Secp256k1hashFingerprintContents.c @@ -0,0 +1,84 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "./CryptoConditions-pkhash.asn" + */ + +#include "Secp256k1hashFingerprintContents.h" + +static int +memb_publicKeyHash_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 20)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static asn_TYPE_member_t asn_MBR_Secp256k1hashFingerprintContents_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct Secp256k1hashFingerprintContents, publicKeyHash), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + memb_publicKeyHash_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "publicKeyHash" + }, +}; +static const ber_tlv_tag_t asn_DEF_Secp256k1hashFingerprintContents_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_Secp256k1hashFingerprintContents_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 } /* publicKeyHash */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_Secp256k1hashFingerprintContents_specs_1 = { + sizeof(struct Secp256k1hashFingerprintContents), + offsetof(struct Secp256k1hashFingerprintContents, _asn_ctx), + asn_MAP_Secp256k1hashFingerprintContents_tag2el_1, + 1, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_Secp256k1hashFingerprintContents = { + "Secp256k1hashFingerprintContents", + "Secp256k1hashFingerprintContents", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_Secp256k1hashFingerprintContents_tags_1, + sizeof(asn_DEF_Secp256k1hashFingerprintContents_tags_1) + /sizeof(asn_DEF_Secp256k1hashFingerprintContents_tags_1[0]), /* 1 */ + asn_DEF_Secp256k1hashFingerprintContents_tags_1, /* Same as above */ + sizeof(asn_DEF_Secp256k1hashFingerprintContents_tags_1) + /sizeof(asn_DEF_Secp256k1hashFingerprintContents_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_Secp256k1hashFingerprintContents_1, + 1, /* Elements count */ + &asn_SPC_Secp256k1hashFingerprintContents_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/Secp256k1hashFingerprintContents.h b/src/cryptoconditions/src/asn/Secp256k1hashFingerprintContents.h new file mode 100644 index 00000000000..b49d7ef7716 --- /dev/null +++ b/src/cryptoconditions/src/asn/Secp256k1hashFingerprintContents.h @@ -0,0 +1,37 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "./CryptoConditions-pkhash.asn" + */ + +#ifndef _Secp256k1hashFingerprintContents_H_ +#define _Secp256k1hashFingerprintContents_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Secp256k1hashFingerprintContents */ +typedef struct Secp256k1hashFingerprintContents { + OCTET_STRING_t publicKeyHash; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} Secp256k1hashFingerprintContents_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_Secp256k1hashFingerprintContents; + +#ifdef __cplusplus +} +#endif + +#endif /* _Secp256k1hashFingerprintContents_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/Secp256k1hashFulfillment.c b/src/cryptoconditions/src/asn/Secp256k1hashFulfillment.c new file mode 100644 index 00000000000..78cc1d9fdcc --- /dev/null +++ b/src/cryptoconditions/src/asn/Secp256k1hashFulfillment.c @@ -0,0 +1,120 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "./CryptoConditions-pkhash.asn" + */ + +#include "Secp256k1hashFulfillment.h" + +static int +memb_publicKey_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 33)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_signature_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 64)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static asn_TYPE_member_t asn_MBR_Secp256k1hashFulfillment_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct Secp256k1hashFulfillment, publicKey), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + memb_publicKey_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "publicKey" + }, + { ATF_NOFLAGS, 0, offsetof(struct Secp256k1hashFulfillment, signature), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + memb_signature_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "signature" + }, +}; +static const ber_tlv_tag_t asn_DEF_Secp256k1hashFulfillment_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_Secp256k1hashFulfillment_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* publicKey */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 } /* signature */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_Secp256k1hashFulfillment_specs_1 = { + sizeof(struct Secp256k1hashFulfillment), + offsetof(struct Secp256k1hashFulfillment, _asn_ctx), + asn_MAP_Secp256k1hashFulfillment_tag2el_1, + 2, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_Secp256k1hashFulfillment = { + "Secp256k1hashFulfillment", + "Secp256k1hashFulfillment", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_Secp256k1hashFulfillment_tags_1, + sizeof(asn_DEF_Secp256k1hashFulfillment_tags_1) + /sizeof(asn_DEF_Secp256k1hashFulfillment_tags_1[0]), /* 1 */ + asn_DEF_Secp256k1hashFulfillment_tags_1, /* Same as above */ + sizeof(asn_DEF_Secp256k1hashFulfillment_tags_1) + /sizeof(asn_DEF_Secp256k1hashFulfillment_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_Secp256k1hashFulfillment_1, + 2, /* Elements count */ + &asn_SPC_Secp256k1hashFulfillment_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/Secp256k1hashFulfillment.h b/src/cryptoconditions/src/asn/Secp256k1hashFulfillment.h new file mode 100644 index 00000000000..b290c95bd09 --- /dev/null +++ b/src/cryptoconditions/src/asn/Secp256k1hashFulfillment.h @@ -0,0 +1,38 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "./CryptoConditions-pkhash.asn" + */ + +#ifndef _Secp256k1hashFulfillment_H_ +#define _Secp256k1hashFulfillment_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Secp256k1hashFulfillment */ +typedef struct Secp256k1hashFulfillment { + OCTET_STRING_t publicKey; + OCTET_STRING_t signature; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} Secp256k1hashFulfillment_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_Secp256k1hashFulfillment; + +#ifdef __cplusplus +} +#endif + +#endif /* _Secp256k1hashFulfillment_H_ */ +#include diff --git a/src/cryptoconditions/src/cryptoconditions.c b/src/cryptoconditions/src/cryptoconditions.c index 8eab9628ec1..451a938f693 100644 --- a/src/cryptoconditions/src/cryptoconditions.c +++ b/src/cryptoconditions/src/cryptoconditions.c @@ -25,6 +25,7 @@ #include "preimage.c" #include "ed25519.c" #include "secp256k1.c" +#include "secp256k1hash.c" #include "anon.c" #include "eval.c" #include "json_rpc.c" @@ -36,7 +37,8 @@ struct CCType *CCTypeRegistry[] = { NULL, /* &CC_rsaType */ &CC_Ed25519Type, &CC_Secp256k1Type, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 6-14 unused */ + &CC_Secp256k1hashType, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 7-14 unused */ &CC_EvalType }; @@ -65,7 +67,7 @@ char *cc_conditionUri(const CC *cond) { unsigned char *fp = calloc(1, 32); cond->type->fingerprint(cond, fp); - unsigned char *encoded = base64_encode(fp, 32); + unsigned char *encoded = base64_encode_url_safe(fp, 32); unsigned char *out = calloc(1, 1000); sprintf(out, "ni:///sha-256;%s?fpt=%s&cost=%lu",encoded, cc_typeName(cond), cc_getCost(cond)); @@ -113,7 +115,7 @@ uint32_t fromAsnSubtypes(const ConditionTypes_t types) { } -size_t cc_conditionBinary(const CC *cond, unsigned char *buf) { +size_t cc_conditionBinary(const CC *cond, unsigned char *buf) { // TODO: make buf size as a param Condition_t *asn = calloc(1, sizeof(Condition_t)); asnCondition(cond, asn); size_t out = 0; @@ -152,31 +154,34 @@ void asnCondition(const CC *cond, Condition_t *asn) { // Fixed previous implementation as it was treating every asn as thresholdSha256 type and it was memory leaking // because SimpleSha256Condition_t types do not have subtypes so it couldn't free it in the end. - int typeId=cond->type->typeId; + // int typeId = cond->type->typeId; if (asn->present==Condition_PR_thresholdSha256 || asn->present==Condition_PR_prefixSha256) { - CompoundSha256Condition_t *sequence=asn->present==Condition_PR_thresholdSha256?&asn->choice.thresholdSha256:&asn->choice.prefixSha256; + CompoundSha256Condition_t* sequence = asn->present == Condition_PR_thresholdSha256 ? &asn->choice.thresholdSha256 : &asn->choice.prefixSha256; sequence->cost = cc_getCost(cond); sequence->fingerprint.buf = calloc(1, 32); - cond->type->fingerprint(cond,sequence->fingerprint.buf); + cond->type->fingerprint(cond, sequence->fingerprint.buf); sequence->fingerprint.size = 32; sequence->subtypes = asnSubtypes(cond->type->getSubtypes(cond)); } else { SimpleSha256Condition_t *choice; + int fingerprintSize; switch (asn->present) { - case Condition_PR_preimageSha256: choice = &asn->choice.preimageSha256; break; - case Condition_PR_rsaSha256: choice = &asn->choice.rsaSha256; break; - case Condition_PR_ed25519Sha256: choice = &asn->choice.ed25519Sha256; break; - case Condition_PR_secp256k1Sha256: choice = &asn->choice.secp256k1Sha256; break; - case Condition_PR_evalSha256: choice = &asn->choice.evalSha256; break; + case Condition_PR_preimageSha256: choice = &asn->choice.preimageSha256; fingerprintSize=32; break; + case Condition_PR_rsaSha256: choice = &asn->choice.rsaSha256; fingerprintSize=32; break; + case Condition_PR_ed25519Sha256: choice = &asn->choice.ed25519Sha256; fingerprintSize=32; break; + case Condition_PR_secp256k1Sha256: choice = &asn->choice.secp256k1Sha256; fingerprintSize=32; break; + case Condition_PR_secp256k1hashSha256: choice = &asn->choice.secp256k1hashSha256; fingerprintSize=20; break; + case Condition_PR_evalSha256: choice = &asn->choice.evalSha256; fingerprintSize=32; break; + default: return; }; choice->cost = cc_getCost(cond); choice->fingerprint.buf = calloc(1, 32); - cond->type->fingerprint(cond,choice->fingerprint.buf); - choice->fingerprint.size = 32; + cond->type->fingerprint(cond, choice->fingerprint.buf); + choice->fingerprint.size = fingerprintSize; } } @@ -271,12 +276,13 @@ int cc_verify(const struct CC *cond, const unsigned char *msg, size_t msgLength, unsigned char targetBinary[1000]; //fprintf(stderr,"in cc_verify cond.%p msg.%p[%d] dohash.%d condbin.%p[%d]\n",cond,msg,(int32_t)msgLength,doHashMsg,condBin,(int32_t)condBinLength); const size_t binLength = cc_conditionBinary(cond, targetBinary); + //printf("%s condBin=%s targetBinary=%s\n", __func__, cc_hex_encode(condBin, condBinLength), cc_hex_encode(targetBinary, binLength)); if (0 != memcmp(condBin, targetBinary, binLength)) { - fprintf(stderr,"cc_verify error A\n"); + fprintf(stderr,"cc_verify error A (condition != fulfillment)\n"); return 0; } if (!cc_ed25519VerifyTree(cond, msg, msgLength)) { - fprintf(stderr,"cc_verify error B\n"); + fprintf(stderr,"cc_verify error B (ed25519Verify)\n"); return 0; } @@ -289,7 +295,12 @@ int cc_verify(const struct CC *cond, const unsigned char *msg, size_t msgLength, //fprintf(stderr," msgHash msglen.%d\n",(int32_t)msgLength); if (!cc_secp256k1VerifyTreeMsg32(cond, msgHash)) { - fprintf(stderr," cc_verify error C\n"); + fprintf(stderr," cc_verify error C (secp256k1 verify error)\n"); + return 0; + } + + if (!cc_secp256k1HashVerifyTreeMsg32(cond, msgHash)) { + fprintf(stderr," cc_verify error C2 (secp256k1hash verify error)\n"); return 0; } @@ -356,9 +367,13 @@ void cc_free(CC *cond) { free(cond); } -CC* cc_copy(CC *cond) { +CC* cc_copy(const CC *cond) { CC *CCcopy=NULL; if (cond) CCcopy=cond->type->copy(cond); return (CCcopy); } + +int cc_hasSubtypes(enum CCTypeId cctypeid) { + return (cctypeid == CC_Threshold || cctypeid == CC_Prefix) ? 1 : 0; +} diff --git a/src/cryptoconditions/src/ed25519.c b/src/cryptoconditions/src/ed25519.c index ad95957dbd9..4051add3f02 100644 --- a/src/cryptoconditions/src/ed25519.c +++ b/src/cryptoconditions/src/ed25519.c @@ -123,6 +123,11 @@ static CC *ed25519FromJSON(const cJSON *params, char *err) { CC *cond = cc_new(CC_Ed25519); cond->publicKey = pk; cond->signature = sig; + + int dontFulfill = 0; + cJSON *obj = cJSON_GetObjectItem(params, "dontFulfill"); + if (obj) cond->dontFulfill = !!obj->valueint; + return cond; } @@ -188,6 +193,7 @@ static CC* ed25519Copy(const CC* cond) condCopy->signature = calloc(1, 64); memcpy(condCopy->signature, cond->signature, 64); } + condCopy->dontFulfill = cond->dontFulfill; return (condCopy); } diff --git a/src/cryptoconditions/src/eval.c b/src/cryptoconditions/src/eval.c index 7e18ef15ae2..80a1633ee69 100644 --- a/src/cryptoconditions/src/eval.c +++ b/src/cryptoconditions/src/eval.c @@ -16,6 +16,7 @@ #include "asn/Condition.h" #include "asn/Fulfillment.h" #include "asn/EvalFulfillment.h" +//#include "asn/EvalFingerprintContents.h" #include "asn/OCTET_STRING.h" //#include "../include/cryptoconditions.h" #include "internal.h" @@ -26,7 +27,18 @@ struct CCType CC_EvalType; static void evalFingerprint(const CC *cond, uint8_t *out) { + + /* TODO: enable for generic evals + if (!cond->includeParamInFP) */ sha256(cond->code, cond->codeLength, out); + /* TODO: enable for generic evals + else { + uint8_t *msg = malloc(cond->codeLength + cond->paramLength); + memcpy(msg, cond->code, cond->codeLength); + memcpy(msg + cond->codeLength, cond->param, cond->paramLength); + sha256(msg, cond->codeLength+cond->paramLength, out); + free(msg); + } */ } @@ -39,23 +51,65 @@ static CC *evalFromJSON(const cJSON *params, char *err) { size_t codeLength; unsigned char *code = 0; - if (!jsonGetBase64(params, "code", err, &code, &codeLength)) { + if (!jsonGetBase64(params, "code", err, &code, &codeLength) && !jsonGetHex(params, "codehex", err, &code, &codeLength) ) { return NULL; } + /* TODO: enable for generic evals + unsigned char *param = NULL; + size_t param_len = 0; + + if (!jsonGetHexOptional(params, "param", err, ¶m, ¶m_len)) { + free(code); + return NULL; + } + + int includeParamInFP = 0; + cJSON *objfp = cJSON_GetObjectItem(params, "includeParamInFP"); + if (objfp) includeParamInFP = !!objfp->valueint; */ + CC *cond = cc_new(CC_Eval); cond->code = code; cond->codeLength = codeLength; + /* TODO: enable for generic evals + cond->param = param; + cond->paramLength = param_len; */ + /* debug: + if (cond->param) { + unsigned char *hex = cc_hex_encode(cond->param, cond->paramLength); + printf("%s cond->param=%s\n", __func__, hex); + free(hex); + }*/ + // cond->includeParamInFP = includeParamInFP; + + int dontFulfill = 0; + cJSON *objdf = cJSON_GetObjectItem(params, "dontFulfill"); + if (objdf) cond->dontFulfill = !!objdf->valueint; + return cond; } static void evalToJSON(const CC *cond, cJSON *code) { - // add code - unsigned char *b64 = base64_encode(cond->code, cond->codeLength); - cJSON_AddItemToObject(code, "code", cJSON_CreateString(b64)); - free(b64); + // now print as hex instead of base64 + //unsigned char *b64 = base64_encode(cond->code, cond->codeLength); + //cJSON_AddItemToObject(code, "code", cJSON_CreateString(b64)); + //free(b64); + + unsigned char *codehex = cc_hex_encode(cond->code, cond->codeLength); + cJSON_AddItemToObject(code, "codehex", cJSON_CreateString(codehex)); + free(codehex); + + /* TODO: enable for generic evals + if (cond->param) { + unsigned char *hex = cc_hex_encode(cond->param, cond->paramLength); + cJSON_AddItemToObject(code, "param", cJSON_CreateString(hex)); + free(hex); + } + if (cond->includeParamInFP) { + cJSON_AddItemToObject(code, "includeParamInFP", cJSON_CreateNumber(cond->includeParamInFP)); + } */ } @@ -69,6 +123,20 @@ static CC *evalFromFulfillment(const Fulfillment_t *ffill) { cond->code = calloc(1,octets.size); memcpy(cond->code, octets.buf, octets.size); + /* TODO: enable for generic evals + cond->param = NULL; + cond->paramLength = 0; + + if (eval->param) { + OCTET_STRING_t paramOctets = *eval->param; + cond->paramLength = paramOctets.size; + //unsigned char *hex = cc_hex_encode(paramOctets.buf, paramOctets.size); + //printf("%s size %ld cond->param=%s\n", __func__, paramOctets.size, hex); + //free(hex); + cond->param = calloc(1, paramOctets.size); + memcpy(cond->param, paramOctets.buf, paramOctets.size); + } */ + return cond; } @@ -78,6 +146,14 @@ static Fulfillment_t *evalToFulfillment(const CC *cond) { ffill->present = Fulfillment_PR_evalSha256; EvalFulfillment_t *eval = &ffill->choice.evalSha256; OCTET_STRING_fromBuf(&eval->code, cond->code, cond->codeLength); + /* TODO: enable for generic evals + if (cond->param) { + eval->param = (OCTET_STRING_t*)calloc(1, sizeof(OCTET_STRING_t)); + OCTET_STRING_fromBuf(eval->param, cond->param, cond->paramLength); + //unsigned char *hex = cc_hex_encode(cond->param, cond->paramLength); + //printf("%s cond->param=%s\n", __func__, hex); + //free(hex); + } */ return ffill; } @@ -89,6 +165,9 @@ int evalIsFulfilled(const CC *cond) { static void evalFree(CC *cond) { free(cond->code); + /* TODO enable for generic evals + if (cond->param) + free(cond->param); */ } @@ -131,11 +210,19 @@ int cc_verifyEval(const CC *cond, VerifyEval verify, void *context) { static CC* evalCopy(const CC* cond) { CC *condCopy = cc_new(CC_Eval); - condCopy->code = calloc(1, cond->codeLength); + condCopy->code = calloc(cond->codeLength, sizeof(uint8_t)); memcpy(condCopy->code, cond->code, cond->codeLength); condCopy->codeLength=cond->codeLength; + + /* TODO enable for generic evals + condCopy->param = NULL; + condCopy->paramLength=cond->paramLength; + if (cond->paramLength) { + condCopy->param = calloc(cond->paramLength, sizeof(uint8_t)); + memcpy(condCopy->param, cond->param, cond->paramLength); + }*/ + condCopy->dontFulfill = cond->dontFulfill; return (condCopy); } - struct CCType CC_EvalType = { 15, "eval-sha-256", Condition_PR_evalSha256, 0, &evalFingerprint, &evalCost, &evalSubtypes, &evalFromJSON, &evalToJSON, &evalFromFulfillment, &evalToFulfillment, &evalIsFulfilled, &evalFree, &evalCopy }; diff --git a/src/cryptoconditions/src/include/ripemd160.c b/src/cryptoconditions/src/include/ripemd160.c new file mode 100644 index 00000000000..c7699a7d0a3 --- /dev/null +++ b/src/cryptoconditions/src/include/ripemd160.c @@ -0,0 +1,342 @@ +/* + * RIPE MD-160 implementation + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * The RIPEMD-160 algorithm was designed by RIPE in 1996 + * http://homes.esat.kuleuven.be/~bosselae/ripemd160.html + * http://ehash.iaik.tugraz.at/wiki/RIPEMD-160 + */ + +#include +#include +#include "ripemd160.h" + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_UINT32_LE +#define GET_UINT32_LE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] ) \ + | ( (uint32_t) (b)[(i) + 1] << 8 ) \ + | ( (uint32_t) (b)[(i) + 2] << 16 ) \ + | ( (uint32_t) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_UINT32_LE +#define PUT_UINT32_LE(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ( ( (n) ) & 0xFF ); \ + (b)[(i) + 1] = (uint8_t) ( ( (n) >> 8 ) & 0xFF ); \ + (b)[(i) + 2] = (uint8_t) ( ( (n) >> 16 ) & 0xFF ); \ + (b)[(i) + 3] = (uint8_t) ( ( (n) >> 24 ) & 0xFF ); \ +} +#endif + +/* + * RIPEMD-160 context setup + */ +void ripemd160_Init(RIPEMD160_CTX *ctx) +{ + memset(ctx, '\0', sizeof(RIPEMD160_CTX)); + ctx->total[0] = 0; + ctx->total[1] = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +#if !defined(MBEDTLS_RIPEMD160_PROCESS_ALT) +/* + * Process one block + */ +void ripemd160_process( RIPEMD160_CTX *ctx, const uint8_t data[RIPEMD160_BLOCK_LENGTH] ) +{ + uint32_t A, B, C, D, E, Ap, Bp, Cp, Dp, Ep, X[16]; + + GET_UINT32_LE( X[ 0], data, 0 ); + GET_UINT32_LE( X[ 1], data, 4 ); + GET_UINT32_LE( X[ 2], data, 8 ); + GET_UINT32_LE( X[ 3], data, 12 ); + GET_UINT32_LE( X[ 4], data, 16 ); + GET_UINT32_LE( X[ 5], data, 20 ); + GET_UINT32_LE( X[ 6], data, 24 ); + GET_UINT32_LE( X[ 7], data, 28 ); + GET_UINT32_LE( X[ 8], data, 32 ); + GET_UINT32_LE( X[ 9], data, 36 ); + GET_UINT32_LE( X[10], data, 40 ); + GET_UINT32_LE( X[11], data, 44 ); + GET_UINT32_LE( X[12], data, 48 ); + GET_UINT32_LE( X[13], data, 52 ); + GET_UINT32_LE( X[14], data, 56 ); + GET_UINT32_LE( X[15], data, 60 ); + + A = Ap = ctx->state[0]; + B = Bp = ctx->state[1]; + C = Cp = ctx->state[2]; + D = Dp = ctx->state[3]; + E = Ep = ctx->state[4]; + +#define F1( x, y, z ) ( x ^ y ^ z ) +#define F2( x, y, z ) ( ( x & y ) | ( ~x & z ) ) +#define F3( x, y, z ) ( ( x | ~y ) ^ z ) +#define F4( x, y, z ) ( ( x & z ) | ( y & ~z ) ) +#define F5( x, y, z ) ( x ^ ( y | ~z ) ) + +#define S( x, n ) ( ( x << n ) | ( x >> (32 - n) ) ) + +#define P( a, b, c, d, e, r, s, f, k ) \ + a += f( b, c, d ) + X[r] + k; \ + a = S( a, s ) + e; \ + c = S( c, 10 ); + +#define P2( a, b, c, d, e, r, s, rp, sp ) \ + P( a, b, c, d, e, r, s, F, K ); \ + P( a ## p, b ## p, c ## p, d ## p, e ## p, rp, sp, Fp, Kp ); + +#define F F1 +#define K 0x00000000 +#define Fp F5 +#define Kp 0x50A28BE6 + P2( A, B, C, D, E, 0, 11, 5, 8 ); + P2( E, A, B, C, D, 1, 14, 14, 9 ); + P2( D, E, A, B, C, 2, 15, 7, 9 ); + P2( C, D, E, A, B, 3, 12, 0, 11 ); + P2( B, C, D, E, A, 4, 5, 9, 13 ); + P2( A, B, C, D, E, 5, 8, 2, 15 ); + P2( E, A, B, C, D, 6, 7, 11, 15 ); + P2( D, E, A, B, C, 7, 9, 4, 5 ); + P2( C, D, E, A, B, 8, 11, 13, 7 ); + P2( B, C, D, E, A, 9, 13, 6, 7 ); + P2( A, B, C, D, E, 10, 14, 15, 8 ); + P2( E, A, B, C, D, 11, 15, 8, 11 ); + P2( D, E, A, B, C, 12, 6, 1, 14 ); + P2( C, D, E, A, B, 13, 7, 10, 14 ); + P2( B, C, D, E, A, 14, 9, 3, 12 ); + P2( A, B, C, D, E, 15, 8, 12, 6 ); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F2 +#define K 0x5A827999 +#define Fp F4 +#define Kp 0x5C4DD124 + P2( E, A, B, C, D, 7, 7, 6, 9 ); + P2( D, E, A, B, C, 4, 6, 11, 13 ); + P2( C, D, E, A, B, 13, 8, 3, 15 ); + P2( B, C, D, E, A, 1, 13, 7, 7 ); + P2( A, B, C, D, E, 10, 11, 0, 12 ); + P2( E, A, B, C, D, 6, 9, 13, 8 ); + P2( D, E, A, B, C, 15, 7, 5, 9 ); + P2( C, D, E, A, B, 3, 15, 10, 11 ); + P2( B, C, D, E, A, 12, 7, 14, 7 ); + P2( A, B, C, D, E, 0, 12, 15, 7 ); + P2( E, A, B, C, D, 9, 15, 8, 12 ); + P2( D, E, A, B, C, 5, 9, 12, 7 ); + P2( C, D, E, A, B, 2, 11, 4, 6 ); + P2( B, C, D, E, A, 14, 7, 9, 15 ); + P2( A, B, C, D, E, 11, 13, 1, 13 ); + P2( E, A, B, C, D, 8, 12, 2, 11 ); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F3 +#define K 0x6ED9EBA1 +#define Fp F3 +#define Kp 0x6D703EF3 + P2( D, E, A, B, C, 3, 11, 15, 9 ); + P2( C, D, E, A, B, 10, 13, 5, 7 ); + P2( B, C, D, E, A, 14, 6, 1, 15 ); + P2( A, B, C, D, E, 4, 7, 3, 11 ); + P2( E, A, B, C, D, 9, 14, 7, 8 ); + P2( D, E, A, B, C, 15, 9, 14, 6 ); + P2( C, D, E, A, B, 8, 13, 6, 6 ); + P2( B, C, D, E, A, 1, 15, 9, 14 ); + P2( A, B, C, D, E, 2, 14, 11, 12 ); + P2( E, A, B, C, D, 7, 8, 8, 13 ); + P2( D, E, A, B, C, 0, 13, 12, 5 ); + P2( C, D, E, A, B, 6, 6, 2, 14 ); + P2( B, C, D, E, A, 13, 5, 10, 13 ); + P2( A, B, C, D, E, 11, 12, 0, 13 ); + P2( E, A, B, C, D, 5, 7, 4, 7 ); + P2( D, E, A, B, C, 12, 5, 13, 5 ); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F4 +#define K 0x8F1BBCDC +#define Fp F2 +#define Kp 0x7A6D76E9 + P2( C, D, E, A, B, 1, 11, 8, 15 ); + P2( B, C, D, E, A, 9, 12, 6, 5 ); + P2( A, B, C, D, E, 11, 14, 4, 8 ); + P2( E, A, B, C, D, 10, 15, 1, 11 ); + P2( D, E, A, B, C, 0, 14, 3, 14 ); + P2( C, D, E, A, B, 8, 15, 11, 14 ); + P2( B, C, D, E, A, 12, 9, 15, 6 ); + P2( A, B, C, D, E, 4, 8, 0, 14 ); + P2( E, A, B, C, D, 13, 9, 5, 6 ); + P2( D, E, A, B, C, 3, 14, 12, 9 ); + P2( C, D, E, A, B, 7, 5, 2, 12 ); + P2( B, C, D, E, A, 15, 6, 13, 9 ); + P2( A, B, C, D, E, 14, 8, 9, 12 ); + P2( E, A, B, C, D, 5, 6, 7, 5 ); + P2( D, E, A, B, C, 6, 5, 10, 15 ); + P2( C, D, E, A, B, 2, 12, 14, 8 ); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F5 +#define K 0xA953FD4E +#define Fp F1 +#define Kp 0x00000000 + P2( B, C, D, E, A, 4, 9, 12, 8 ); + P2( A, B, C, D, E, 0, 15, 15, 5 ); + P2( E, A, B, C, D, 5, 5, 10, 12 ); + P2( D, E, A, B, C, 9, 11, 4, 9 ); + P2( C, D, E, A, B, 7, 6, 1, 12 ); + P2( B, C, D, E, A, 12, 8, 5, 5 ); + P2( A, B, C, D, E, 2, 13, 8, 14 ); + P2( E, A, B, C, D, 10, 12, 7, 6 ); + P2( D, E, A, B, C, 14, 5, 6, 8 ); + P2( C, D, E, A, B, 1, 12, 2, 13 ); + P2( B, C, D, E, A, 3, 13, 13, 6 ); + P2( A, B, C, D, E, 8, 14, 14, 5 ); + P2( E, A, B, C, D, 11, 11, 0, 15 ); + P2( D, E, A, B, C, 6, 8, 3, 13 ); + P2( C, D, E, A, B, 15, 5, 9, 11 ); + P2( B, C, D, E, A, 13, 6, 11, 11 ); +#undef F +#undef K +#undef Fp +#undef Kp + + C = ctx->state[1] + C + Dp; + ctx->state[1] = ctx->state[2] + D + Ep; + ctx->state[2] = ctx->state[3] + E + Ap; + ctx->state[3] = ctx->state[4] + A + Bp; + ctx->state[4] = ctx->state[0] + B + Cp; + ctx->state[0] = C; +} +#endif /* !MBEDTLS_RIPEMD160_PROCESS_ALT */ + +/* + * RIPEMD-160 process buffer + */ +void ripemd160_Update( RIPEMD160_CTX *ctx, const uint8_t *input, uint32_t ilen ) +{ + uint32_t fill; + uint32_t left; + + if( ilen == 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = RIPEMD160_BLOCK_LENGTH - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (uint32_t) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), input, fill ); + ripemd160_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= RIPEMD160_BLOCK_LENGTH ) + { + ripemd160_process( ctx, input ); + input += RIPEMD160_BLOCK_LENGTH; + ilen -= RIPEMD160_BLOCK_LENGTH; + } + + if( ilen > 0 ) + { + memcpy( (void *) (ctx->buffer + left), input, ilen ); + } +} + +static const uint8_t ripemd160_padding[RIPEMD160_BLOCK_LENGTH] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * RIPEMD-160 final digest + */ +void ripemd160_Final( RIPEMD160_CTX *ctx, uint8_t output[RIPEMD160_DIGEST_LENGTH] ) +{ + uint32_t last, padn; + uint32_t high, low; + uint8_t msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_LE( low, msglen, 0 ); + PUT_UINT32_LE( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + ripemd160_Update( ctx, ripemd160_padding, padn ); + ripemd160_Update( ctx, msglen, 8 ); + + PUT_UINT32_LE( ctx->state[0], output, 0 ); + PUT_UINT32_LE( ctx->state[1], output, 4 ); + PUT_UINT32_LE( ctx->state[2], output, 8 ); + PUT_UINT32_LE( ctx->state[3], output, 12 ); + PUT_UINT32_LE( ctx->state[4], output, 16 ); + + memset(ctx, '\0', sizeof(RIPEMD160_CTX)); +} + +/* + * output = RIPEMD-160( input buffer ) + */ +void ripemd160(const uint8_t *msg, uint32_t msg_len, uint8_t hash[RIPEMD160_DIGEST_LENGTH]) +{ + RIPEMD160_CTX ctx; + ripemd160_Init( &ctx ); + ripemd160_Update( &ctx, msg, msg_len ); + ripemd160_Final( &ctx, hash ); +} \ No newline at end of file diff --git a/src/cryptoconditions/src/include/ripemd160.h b/src/cryptoconditions/src/include/ripemd160.h new file mode 100644 index 00000000000..c30a3a1aecc --- /dev/null +++ b/src/cryptoconditions/src/include/ripemd160.h @@ -0,0 +1,22 @@ +#ifndef __RIPEMD160_H__ +#define __RIPEMD160_H__ + +#include + +#define RIPEMD160_BLOCK_LENGTH 64 +#define RIPEMD160_DIGEST_LENGTH 20 + +typedef struct _RIPEMD160_CTX { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[5]; /*!< intermediate digest state */ + uint8_t buffer[RIPEMD160_BLOCK_LENGTH]; /*!< data block being processed */ +} RIPEMD160_CTX; + +void ripemd160_Init(RIPEMD160_CTX *ctx); +void ripemd160_Update(RIPEMD160_CTX *ctx, const uint8_t *input, uint32_t ilen); +void ripemd160_Final(RIPEMD160_CTX *ctx, + uint8_t output[RIPEMD160_DIGEST_LENGTH]); +void ripemd160(const uint8_t *msg, uint32_t msg_len, + uint8_t hash[RIPEMD160_DIGEST_LENGTH]); + +#endif \ No newline at end of file diff --git a/src/cryptoconditions/src/internal.h b/src/cryptoconditions/src/internal.h index 649ef547cf4..dcb0e4f19f3 100644 --- a/src/cryptoconditions/src/internal.h +++ b/src/cryptoconditions/src/internal.h @@ -85,6 +85,8 @@ 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 *base64_encode_url_safe(const unsigned char *data, size_t input_length); +unsigned char *base64_decode_url_safe(const unsigned char *data_, size_t *output_length); 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); diff --git a/src/cryptoconditions/src/prefix.c b/src/cryptoconditions/src/prefix.c index d1979126ad3..2606cdf5d09 100644 --- a/src/cryptoconditions/src/prefix.c +++ b/src/cryptoconditions/src/prefix.c @@ -109,6 +109,9 @@ static CC *prefixFromJSON(const cJSON *params, char *err) { cc_free(cond); return NULL; } + int dontFulfill = 0; + cJSON *obj = cJSON_GetObjectItem(params, "dontFulfill"); + if (obj) cond->dontFulfill = !!obj->valueint; return cond; } @@ -141,6 +144,7 @@ static CC* prefixCopy(const CC* cond) memcpy(condCopy->prefix, cond->prefix, cond->prefixLength); condCopy->prefixLength = cond->prefixLength; condCopy->subcondition = cond->subcondition->type->copy(cond->subcondition); + condCopy->dontFulfill = cond->dontFulfill; return (condCopy); } diff --git a/src/cryptoconditions/src/preimage.c b/src/cryptoconditions/src/preimage.c index 8853abd47e3..185b496a2eb 100644 --- a/src/cryptoconditions/src/preimage.c +++ b/src/cryptoconditions/src/preimage.c @@ -30,6 +30,10 @@ static CC *preimageFromJSON(const cJSON *params, char *err) { free(cond); return NULL; } + int dontFulfill = 0; + cJSON *obj = cJSON_GetObjectItem(params, "dontFulfill"); + if (obj) cond->dontFulfill = !!obj->valueint; + return cond; } @@ -88,6 +92,7 @@ static CC* preimageCopy(const CC* cond) condCopy->preimage = calloc(1, cond->preimageLength); memcpy(condCopy->preimage, cond->preimage, cond->preimageLength); condCopy->preimageLength = cond->preimageLength; + condCopy->dontFulfill = cond->dontFulfill; return (condCopy); } diff --git a/src/cryptoconditions/src/secp256k1.c b/src/cryptoconditions/src/secp256k1.c index 3255972c7a3..e327008fdfd 100644 --- a/src/cryptoconditions/src/secp256k1.c +++ b/src/cryptoconditions/src/secp256k1.c @@ -106,10 +106,13 @@ int secp256k1Verify(CC *cond, CCVisitor visitor) { rc = secp256k1_ec_pubkey_parse(ec_ctx_verify, &pk, cond->publicKey, SECP256K1_PK_SIZE); if (rc != 1) return 0; - // parse siganature + // parse signature secp256k1_ecdsa_signature sig; rc = secp256k1_ecdsa_signature_parse_compact(ec_ctx_verify, &sig, cond->signature); if (rc != 1) return 0; + //unsigned char *hex = cc_hex_encode(cond->publicKey, SECP256K1_PK_SIZE); + //printf("%s using publicKey %s\n", __func__, hex); + //free(hex); // Only accepts lower S signatures rc = secp256k1_ecdsa_verify(ec_ctx_verify, &sig, visitor.msg, &pk); @@ -195,13 +198,13 @@ int cc_signTreeSecp256k1Msg32(CC *cond, const unsigned char *privateKey, const u unsigned char publicKey[SECP256K1_PK_SIZE]; size_t ol = SECP256K1_PK_SIZE; secp256k1_ec_pubkey_serialize(ec_ctx_verify, publicKey, &ol, &spk, SECP256K1_EC_COMPRESSED); - if ( 0 ) + /*if ( 0 ) { int32_t z; for (z=0; z<33; z++) fprintf(stderr,"%02x",publicKey[z]); fprintf(stderr," pubkey\n"); - } + }*/ // sign CCSecp256k1SigningData signing = {publicKey, privateKey, 0}; CCVisitor visitor = {&secp256k1Sign, msg32, 32, &signing}; @@ -259,6 +262,11 @@ static CC *secp256k1FromJSON(const cJSON *params, char *err) { if (!cond) { strcpy(err, "invalid public key"); } + + int dontFulfill = 0; + cJSON *obj = cJSON_GetObjectItem(params, "dontFulfill"); + if (obj) cond->dontFulfill = !!obj->valueint; + END: free(pk); free(sig); @@ -290,7 +298,13 @@ static Fulfillment_t *secp256k1ToFulfillment(const CC *cond, FulfillmentFlags _f Secp256k1Fulfillment_t *sec = &ffill->choice.secp256k1Sha256; OCTET_STRING_fromBuf(&sec->publicKey, cond->publicKey, SECP256K1_PK_SIZE); + //if (cond->signature) OCTET_STRING_fromBuf(&sec->signature, cond->signature, SECP256K1_SIG_SIZE); + /*else { + uint8_t *fakesig = (uint8_t*)calloc(1, SECP256K1_SIG_SIZE); + OCTET_STRING_fromBuf(&sec->signature, fakesig, SECP256K1_SIG_SIZE); + free(fakesig); + }*/ return ffill; } @@ -316,6 +330,7 @@ static CC* secp256k1Copy(const CC* cond) condCopy->signature = calloc(1, SECP256K1_SIG_SIZE); memcpy(condCopy->signature, cond->signature, SECP256K1_SIG_SIZE); } + condCopy->dontFulfill = cond->dontFulfill; return (condCopy); } diff --git a/src/cryptoconditions/src/secp256k1hash.c b/src/cryptoconditions/src/secp256k1hash.c new file mode 100644 index 00000000000..b5e54d73f75 --- /dev/null +++ b/src/cryptoconditions/src/secp256k1hash.c @@ -0,0 +1,364 @@ +/****************************************************************************** + * 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. * + * * + ******************************************************************************/ + +#define _GNU_SOURCE 1 + +#if __linux +#include +#elif defined(_WIN32) || defined(_WIN64) +#include +#endif + +#include +#include + +#include "sha256.h" +#include "ripemd160.h" + +#include "asn/Condition.h" +#include "asn/Fulfillment.h" +#include "asn/Secp256k1hashFulfillment.h" +#include "asn/Secp256k1hashFingerprintContents.h" +#include "asn/OCTET_STRING.h" +//#include +#include "include/secp256k1/include/secp256k1.h" +//#include "../include/cryptoconditions.h" +#include "internal.h" + + +struct CCType CC_Secp256k1hashType; + +static const size_t SECP256K1_HASH_SIZE = 20; + + +// use secp256k1 ctx functions +extern secp256k1_context *ec_ctx_sign, *ec_ctx_verify; +extern pthread_mutex_t cc_secp256k1ContextLock; +void lockSign(); +void unlockSign(); +void initVerify(); + +// we dont use cryptocondition default hash sha256 function +// use btc address sha256+ripemd160 +// for publicKeyHash the hash is the value itself +static void secp256k1hashFingerprint(const CC *cond, uint8_t *out) { + + //Secp256k1hashFingerprintContents_t *fp = calloc(1, sizeof(Secp256k1hashFingerprintContents_t)); + if (cond->publicKey) { // set by fulfillment + // make the pubkey hash from the pubkey + uint8_t pksha256[32]; + uint8_t pkripemd160[SECP256K1_HASH_SIZE]; + sha256(cond->publicKey, SECP256K1_PK_SIZE, pksha256); + ripemd160(pksha256, sizeof(pksha256), pkripemd160); + //OCTET_STRING_fromBuf(&fp->publicKeyHash, pkripemd160, SECP256K1_HASH_SIZE); + + //unsigned char *hex = cc_hex_encode(pkripemd160, SECP256K1_HASH_SIZE); + //printf("%s pkripemd160 %s\n", __func__, hex); + //free(hex); + memcpy(out, pkripemd160, SECP256K1_HASH_SIZE); + } + else { + //OCTET_STRING_fromBuf(&fp->publicKeyHash, cond->publicKeyHash, SECP256K1_HASH_SIZE); + + //unsigned char *hex = cc_hex_encode(cond->publicKeyHash, SECP256K1_HASH_SIZE); + //printf("%s copying publicKeyHash %s\n", __func__, hex); + //free(hex); + memcpy(out, cond->publicKeyHash, SECP256K1_HASH_SIZE); + } + + // as a fingerprint we use sha256+ripemd160 if the cond is fulfilled and has a pubkey or the value itself if the cond has a pubkeyHash + // hashFingerprintContents(&asn_DEF_Secp256k1hashFingerprintContents, fp, out); +} + + +int secp256k1hashVerify(CC *cond, CCVisitor visitor) { + if (cond->type->typeId != CC_Secp256k1hashType.typeId) return 1; + initVerify(); + + int rc; + + // parse pubkey + secp256k1_pubkey pk; + rc = secp256k1_ec_pubkey_parse(ec_ctx_verify, &pk, cond->publicKey, SECP256K1_PK_SIZE); + if (rc != 1) return 0; + //unsigned char *hex = cc_hex_encode(cond->publicKey, SECP256K1_PK_SIZE); + //printf("%s using publicKey %s\n", __func__, hex); + //free(hex); + + // parse signature + secp256k1_ecdsa_signature sig; + rc = secp256k1_ecdsa_signature_parse_compact(ec_ctx_verify, &sig, cond->signature); + if (rc != 1) return 0; + + // Only accepts lower S signatures + rc = secp256k1_ecdsa_verify(ec_ctx_verify, &sig, visitor.msg, &pk); + if (rc != 1) return 0; + + return 1; +} + + +int cc_secp256k1HashVerifyTreeMsg32(const CC *cond, const unsigned char *msg32) { + int subtypes = cc_typeMask(cond); + if (subtypes & (1 << CC_PrefixType.typeId) && + subtypes & (1 << CC_Secp256k1hashType.typeId)) { + // No support for prefix currently, due to pending protocol decision on + // how to combine message and prefix into 32 byte hash + return 0; + } + CCVisitor visitor = {&secp256k1hashVerify, msg32, 0, NULL}; + int out = cc_visit(cond, visitor); + return out; +} + + +/* + * Signing data + */ +typedef struct CCSecp256k1HashSigningData { + const unsigned char *pkhash; + const unsigned char *pk; + const unsigned char *sk; + int nSigned; +} CCSecp256k1HashSigningData; + + +/* + * Visitor that signs an secp256k1 condition if it has a matching public key hash + * also adds the pubkey from the privkey + */ +static int secp256k1hashSign(CC *cond, CCVisitor visitor) { + if (cond->type->typeId != CC_Secp256k1hash) return 1; + CCSecp256k1HashSigningData *signing = (CCSecp256k1HashSigningData*) visitor.context; + + if (0 != memcmp(cond->publicKeyHash, signing->pkhash, SECP256K1_HASH_SIZE)) return 1; + + secp256k1_ecdsa_signature sig; + lockSign(); + int rc = secp256k1_ecdsa_sign(ec_ctx_sign, &sig, visitor.msg, signing->sk, NULL, NULL); + unlockSign(); + + if (rc != 1) + { + fprintf(stderr,"secp256k1hashSign rc.%d\n",rc); + return 0; + } + + if (!cond->publicKey) cond->publicKey = calloc(1, SECP256K1_PK_SIZE); + memcpy(cond->publicKey, signing->pk, SECP256K1_PK_SIZE); // add signed pk to allow to create fulfillment + if (!cond->signature) cond->signature = calloc(1, SECP256K1_SIG_SIZE); + secp256k1_ecdsa_signature_serialize_compact(ec_ctx_verify, cond->signature, &sig); + + signing->nSigned++; + return 1; +} + + +/* + * Sign secp256k1 conditions in a tree + */ +int cc_signTreeSecp256k1HashMsg32(CC *cond, const unsigned char *privateKey, const unsigned char *msg32) { + if (cc_typeMask(cond) & (1 << CC_Prefix)) { + // No support for prefix currently, due to pending protocol decision on + // how to combine message and prefix into 32 byte hash + return 0; + } + + // derive the pubkey + secp256k1_pubkey spk; + lockSign(); + int rc = secp256k1_ec_pubkey_create(ec_ctx_sign, &spk, privateKey); + unlockSign(); + if (rc != 1) { + fprintf(stderr, "Cryptoconditions couldn't derive secp256k1 pubkey\n"); + return 0; + } + + // serialize pubkey + //unsigned char *publicKey = calloc(1, SECP256K1_PK_SIZE); + unsigned char publicKey[SECP256K1_PK_SIZE]; + size_t ol = SECP256K1_PK_SIZE; + secp256k1_ec_pubkey_serialize(ec_ctx_verify, publicKey, &ol, &spk, SECP256K1_EC_COMPRESSED); + /*if ( 0 ) + { + int32_t z; + for (z=0; z<33; z++) + fprintf(stderr,"%02x",publicKey[z]); + fprintf(stderr," pubkey\n"); + }*/ + uint8_t pksha256[32]; + uint8_t pkripemd160[SECP256K1_HASH_SIZE]; + sha256(publicKey, SECP256K1_PK_SIZE, pksha256); + ripemd160(pksha256, sizeof(pksha256), pkripemd160); + + // sign + CCSecp256k1HashSigningData signing = {pkripemd160, publicKey, privateKey, 0}; + CCVisitor visitor = {&secp256k1hashSign, msg32, 32, &signing}; + cc_visit(cond, visitor); + + //free(publicKey); + return signing.nSigned; +} + + +static unsigned long secp256k1hashCost(const CC *cond) { + return 131072; +} + +static CC *cc_secp256k1hashCondition(const unsigned char *publicKeyHash, const unsigned char *publicKey, const unsigned char *signature) { + unsigned char *pk = NULL, *sig = NULL; + unsigned char *pkhash = NULL; + + // Check that pk parses + initVerify(); + if (publicKey) { + secp256k1_pubkey spk; + int rc = secp256k1_ec_pubkey_parse(ec_ctx_verify, &spk, publicKey, SECP256K1_PK_SIZE); + if (!rc) { + return NULL; + } + pk = calloc(1, SECP256K1_PK_SIZE); + memcpy(pk, publicKey, SECP256K1_PK_SIZE); + } + + if (signature) { + sig = calloc(1, SECP256K1_SIG_SIZE); + memcpy(sig, signature, SECP256K1_SIG_SIZE); + } + + if (publicKeyHash) { + pkhash = calloc(1, SECP256K1_HASH_SIZE); + memcpy(pkhash, publicKeyHash, SECP256K1_HASH_SIZE); + } + if (!pk && !pkhash) return NULL; + + CC *cond = cc_new(CC_Secp256k1hash); + cond->publicKey = pk; + cond->signature = sig; + cond->publicKeyHash = pkhash; + return cond; +} + +static CC *secp256k1hashFromJSON(const cJSON *params, char *err) { + CC *cond = NULL; + unsigned char *pkhash = NULL; + unsigned char *pk = NULL, *sig = NULL; + size_t pkhashSize; + size_t pkSize, sigSize; + + // try get pk + jsonGetHexOptional(params, "publicKey", err, &pk, &pkSize); + // try get sig + jsonGetHexOptional(params, "signature", err, &sig, &sigSize); + if (sig && SECP256K1_SIG_SIZE != sigSize) { + strcpy(err, "signature has incorrect length"); + goto END; + } + + jsonGetHex(params, "publicKeyHash", err, &pkhash, &pkhashSize); + if (pkhash && pkhashSize != SECP256K1_HASH_SIZE) { strcpy(err, "invalid public key hash"); goto END; } + + if (!pkhash && !pk) { strcpy(err, "invalid public key or hash"); goto END; } + + cond = cc_secp256k1hashCondition(pkhash, pk, sig); + if (!cond) { + strcpy(err, "invalid secp256k1hash data"); + } + int dontFulfill = 0; + cJSON *obj = cJSON_GetObjectItem(params, "dontFulfill"); + if (obj) cond->dontFulfill = !!obj->valueint; +END: + if (pkhash) free(pkhash); + if (pk) free(pk); + if (sig) free(sig); + + return cond; +} + +static void secp256k1hashToJSON(const CC *cond, cJSON *params) { + if (cond->publicKeyHash) { + jsonAddHex(params, "publicKeyHash", cond->publicKeyHash, SECP256K1_HASH_SIZE); + } + if (cond->publicKey) { + jsonAddHex(params, "publicKey", cond->publicKey, SECP256K1_PK_SIZE); + } + if (cond->signature) { + jsonAddHex(params, "signature", cond->signature, SECP256K1_SIG_SIZE); + } +} + + +static CC *secp256k1hashFromFulfillment(const Fulfillment_t *ffill, FulfillmentFlags _flags) { + return cc_secp256k1hashCondition(NULL, + ffill->choice.secp256k1Sha256.publicKey.buf, + ffill->choice.secp256k1Sha256.signature.buf); +} + + +static Fulfillment_t *secp256k1hashToFulfillment(const CC *cond, FulfillmentFlags _flags) { + if (!cond->signature || !cond->publicKey) { + return NULL; + } + + Fulfillment_t *ffill = calloc(1, sizeof(Fulfillment_t)); + ffill->present = Fulfillment_PR_secp256k1hashSha256; + Secp256k1Fulfillment_t *sec = &ffill->choice.secp256k1hashSha256; + + OCTET_STRING_fromBuf(&sec->publicKey, cond->publicKey, SECP256K1_PK_SIZE); + OCTET_STRING_fromBuf(&sec->signature, cond->signature, SECP256K1_SIG_SIZE); + return ffill; +} + + +int secp256k1hashIsFulfilled(const CC *cond) { + return cond->signature != NULL; +} + +static void secp256k1hashFree(CC *cond) { + if (cond->publicKey) + free(cond->publicKey); + if (cond->publicKeyHash) + free(cond->publicKeyHash); + if (cond->signature) { + free(cond->signature); + } +} + +static CC* secp256k1hashCopy(const CC* cond) +{ + CC *condCopy = cc_new(CC_Secp256k1hash); + if (cond->publicKey) { + condCopy->publicKey = calloc(1, SECP256K1_PK_SIZE); + memcpy(condCopy->publicKey, cond->publicKey, SECP256K1_PK_SIZE); + } + if (cond->publicKeyHash) { + condCopy->publicKeyHash = calloc(1, SECP256K1_HASH_SIZE); + memcpy(condCopy->publicKeyHash, cond->publicKeyHash, SECP256K1_HASH_SIZE); + } + if (cond->signature) { + condCopy->signature = calloc(1, SECP256K1_SIG_SIZE); + memcpy(condCopy->signature, cond->signature, SECP256K1_SIG_SIZE); + } + condCopy->dontFulfill = cond->dontFulfill; + return (condCopy); +} + + +static uint32_t secp256k1hashSubtypes(const CC *cond) { + return 0; +} + + +struct CCType CC_Secp256k1hashType = { 6, "secp256k1hash-sha-256", Condition_PR_secp256k1hashSha256, 0, &secp256k1hashFingerprint, &secp256k1hashCost, &secp256k1hashSubtypes, &secp256k1hashFromJSON, &secp256k1hashToJSON, &secp256k1hashFromFulfillment, &secp256k1hashToFulfillment, &secp256k1hashIsFulfilled, &secp256k1hashFree, &secp256k1hashCopy }; diff --git a/src/cryptoconditions/src/threshold.c b/src/cryptoconditions/src/threshold.c index 82aeca4c85e..f29b614182a 100644 --- a/src/cryptoconditions/src/threshold.c +++ b/src/cryptoconditions/src/threshold.c @@ -124,7 +124,7 @@ static int cmpConditionCost(const void *a, const void *b) { static CC *thresholdFromFulfillmentMixed(const Fulfillment_t *ffill) { ThresholdFulfillment_t *t = ffill->choice.thresholdSha256; - FulfillmentFlags flags = 0; + FulfillmentFlags flags = MixedMode; Fulfillment_t** arrFulfills = t->subfulfillments.list.array; size_t nffills = t->subfulfillments.list.count; @@ -134,11 +134,16 @@ static CC *thresholdFromFulfillmentMixed(const Fulfillment_t *ffill) { if (nffills == 0) { free(cond); + //fprintf(stderr, "%s nffills == 0\n", __func__); return NULL; } { // Get the real threshold from the first ffill CC *tc = fulfillmentToCC(arrFulfills[0], flags); + if (tc == NULL) { + free(cond); + return NULL; + } if (tc->type->typeId != CC_Preimage || tc->preimageLength != 1) { cc_free(tc); free(cond); @@ -172,12 +177,12 @@ static CC *thresholdFromFulfillmentMixed(const Fulfillment_t *ffill) { return NULL; } } - return cond; } static CC *thresholdFromFulfillment(const Fulfillment_t *ffill, FulfillmentFlags flags) { + //printf("%s flags & MixedMode %d\n", __func__, (flags & MixedMode)); if (flags & MixedMode) return thresholdFromFulfillmentMixed(ffill); ThresholdFulfillment_t *t = ffill->choice.thresholdSha256; @@ -194,7 +199,8 @@ static CC *thresholdFromFulfillment(const Fulfillment_t *ffill, FulfillmentFlags if (!subconditions[i]) { for (int j=0; jcode = calloc(1, 2); - t->code[0] = cond->threshold; + t->code[0] = cond->threshold; // store threshold value in a special purpose preimage cond t->preimageLength = 1; asn_set_add(&tf->subfulfillments, asnFulfillmentNew(t, flags)); for (int i=0; isize; i++) { CC *sub = cond->subconditions[i]; if (fulfillment = asnFulfillmentNew(sub, flags)) { - asn_set_add(&tf->subfulfillments, fulfillment); + asn_set_add(&tf->subfulfillments, fulfillment); // always first try to add as fulfillment } else { asn_set_add(&tf->subconditions, asnConditionNew(sub)); } @@ -249,11 +255,11 @@ static Fulfillment_t *thresholdToFulfillment(const CC *cond, FulfillmentFlags fl for (int i=0; isize; i++) { CC *sub = subconditions[i]; - if (needed && (fulfillment = asnFulfillmentNew(sub, flags))) { - asn_set_add(&tf->subfulfillments, fulfillment); + if (needed && !sub->dontFulfill && (fulfillment = asnFulfillmentNew(sub, flags))) { + asn_set_add(&tf->subfulfillments, fulfillment); // add as a fulfillment for as many as the thershold number needed--; } else { - asn_set_add(&tf->subconditions, asnConditionNew(sub)); + asn_set_add(&tf->subconditions, asnConditionNew(sub)); // the rest add as anon conds } } @@ -289,11 +295,15 @@ static CC *thresholdFromJSON(const cJSON *params, char *err) { cond->size = cJSON_GetArraySize(subfulfillments_item); cond->subconditions = calloc(cond->size, sizeof(CC*)); + int dontFulfill = 0; + cJSON *obj = cJSON_GetObjectItem(params, "dontFulfill"); + if (obj) cond->dontFulfill = !!obj->valueint; + cJSON *sub; for (int i=0; isize; i++) { sub = cJSON_GetArrayItem(subfulfillments_item, i); cond->subconditions[i] = cc_conditionFromJSON(sub, err); - if (err[0] || cond->subconditions[i]==NULL) + if (/*err[0] || */ cond->subconditions[i]==NULL) // it should not be any 'err' if subconditions was created okay. This 'err[0]' check caused cc_conditionFromJSON failure if err not inited by the user { if (cond) cc_free(cond); return NULL; @@ -344,6 +354,7 @@ static CC* thresholdCopy(const CC* cond) for (int i=0; isize; i++) { condCopy->subconditions[i]=cond->subconditions[i]->type->copy(cond->subconditions[i]); } + condCopy->dontFulfill = cond->dontFulfill; return (condCopy); } diff --git a/src/cryptoconditions/src/utils.c b/src/cryptoconditions/src/utils.c index 26d5f706976..dd07dd9e993 100644 --- a/src/cryptoconditions/src/utils.c +++ b/src/cryptoconditions/src/utils.c @@ -72,17 +72,11 @@ unsigned char *base64_encode(const unsigned char *data, size_t input_length) { // make sure there's a null termination for string protocol encoded_data[output_length] = '\0'; - - // url safe - for (int i=0; i payouts, uint32_t nExpiryHeightOverride) { @@ -317,7 +314,7 @@ bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &pegstxid,uint256 &tokeni /* * Required by main */ -CAmount GetCoinImportValue(const CTransaction &tx) +CAmount GetCoinImportValue(const CTransaction &tx, int64_t nTime, int32_t nHeight) { ImportProof proof; CTransaction burnTx; std::vector payouts; bool isNewImportTx = false; @@ -348,11 +345,19 @@ CAmount GetCoinImportValue(const CTransaction &tx) if (!vnonfungibleOpret.empty()) nonfungibleEvalCode = vnonfungibleOpret.begin()[0]; + + std::vector vDeadPubkeys = GetBurnPubKeys(nTime, nHeight); + + // calc outputs for burn tx int64_t ccBurnOutputs = 0; for (auto v : burnTx.vout) if (v.scriptPubKey.IsPayToCryptoCondition() && - CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(nonfungibleEvalCode, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY)))) // burned to dead pubkey + std::find_if(vDeadPubkeys.begin(), vDeadPubkeys.end(), [v, nonfungibleEvalCode](const CPubKey &burnpk) { + return IsEqualDestinations(v.scriptPubKey, CCPubKey(CCwrapper(MakeTokensCCcond1(nonfungibleEvalCode, burnpk)).get() )); + } ) != vDeadPubkeys.end()) + + //CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(nonfungibleEvalCode, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY_FIXED)))) // burned to dead pubkey ccBurnOutputs += v.nValue; return ccBurnOutputs + burnTx.vout.back().nValue; // total token burned value diff --git a/src/importcoin.h b/src/importcoin.h index 0f7c52aa520..79672934f8c 100644 --- a/src/importcoin.h +++ b/src/importcoin.h @@ -92,7 +92,7 @@ class ImportProof { -CAmount GetCoinImportValue(const CTransaction &tx); +CAmount GetCoinImportValue(const CTransaction &tx, int64_t nTime, int32_t nHeight); CTransaction MakeImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector payouts, uint32_t nExpiryHeightOverride = 0); CTransaction MakePegsImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector payouts, uint32_t nExpiryHeightOverride = 0); @@ -122,4 +122,7 @@ bool CheckVinPubKey(const CTransaction &sourcetx, int32_t i, uint8_t pubkey33[33 CMutableTransaction MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount); int32_t GetSelfimportProof(const CMutableTransaction sourceMtx, CMutableTransaction &templateMtx, ImportProof &proofNull); +extern std::string ASSETCHAINS_SELFIMPORT; +extern uint16_t ASSETCHAINS_CODAPORT, ASSETCHAINS_BEAMPORT; + #endif /* IMPORTCOIN_H */ \ No newline at end of file diff --git a/src/init.cpp b/src/init.cpp index 052192c42a2..bc0845f7ebd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -23,6 +23,7 @@ #endif #include "init.h" +#include "alert.h" #include "crypto/common.h" #include "primitives/block.h" #include "addrman.h" @@ -35,6 +36,7 @@ #include "httprpc.h" #include "key.h" #include "notarisationdb.h" +#include "komodo_version.h" #ifdef ENABLE_MINING #include "key_io.h" @@ -90,17 +92,13 @@ #include "librustzcash.h" +#ifdef ENABLE_WEBSOCKETS +#include "komodo_websockets.h" +#endif + using namespace std; #include "komodo_defs.h" -extern void ThreadSendAlert(); -extern bool komodo_dailysnapshot(int32_t height); -extern int32_t KOMODO_LOADINGBLOCKS; -extern bool VERUS_MINTBLOCKS; -extern char ASSETCHAINS_SYMBOL[]; -extern int32_t KOMODO_SNAPSHOT_INTERVAL; - -extern void komodo_init(int32_t height); ZCJoinSplit* pzcashParams = NULL; @@ -230,6 +228,9 @@ void Shutdown() mempool.AddTransactionsUpdated(1); StopHTTPRPC(); +#ifdef ENABLE_WEBSOCKETS + ws::StopWebSockets(); +#endif StopREST(); StopRPC(); StopHTTPServer(); @@ -754,10 +755,6 @@ void ThreadNotifyRecentlyAdded() } } -/* declarations needed for ThreadUpdateKomodoInternals */ -void komodo_passport_iteration(); -void komodo_cbopretupdate(int32_t forceflag); - void ThreadUpdateKomodoInternals() { RenameThread("int-updater"); @@ -913,6 +910,10 @@ bool AppInitServers(boost::thread_group& threadGroup) return false; if (!StartHTTPRPC()) return false; +#ifdef ENABLE_WEBSOCKETS + if (!ws::StartWebSockets(threadGroup)) + return false; +#endif if (GetBoolArg("-rest", false) && !StartREST()) return false; if (!StartHTTPServer()) @@ -923,8 +924,6 @@ bool AppInitServers(boost::thread_group& threadGroup) /** Initialize bitcoin. * @pre Parameters should be parsed and config file should be read. */ -extern int32_t KOMODO_REWIND; - bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) { // ********************************************************* Step 1: setup @@ -1009,7 +1008,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fLogIPs = GetBoolArg("-logips", false); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - LogPrintf("Zcash version %s (%s)\n", FormatFullVersion(), CLIENT_DATE); + LogPrintf("Zcash version %s\n", FormatFullVersion()); // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified @@ -1383,7 +1382,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (GetBoolArg("-shrinkdebugfile", !fDebug)) ShrinkDebugFile(); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - LogPrintf("Komodo version %s (%s)\n", FormatFullVersion(), CLIENT_DATE); + LogPrintf("Komodo version %s\n", FormatVersion(KOMODO_VERSION)); + LogPrintf("Tokel version %s (%s)\n", FormatVersion(TOKEL_VERSION), CLIENT_DATE); if (fPrintToDebugLog) OpenDebugLog(); @@ -1475,7 +1475,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) return InitError(strprintf("User Agent comment (%s) contains unsafe characters.", cmt)); uacomments.push_back(SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)); } - strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments); + uacomments.insert(uacomments.begin(), TOKEL_CLIENT_NAME); + strSubVersion = FormatSubVersion(CLIENT_NAME, KOMODO_VERSION, uacomments); if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) { return InitError(strprintf("Total length of network version string %i exceeds maximum of %i characters. Reduce the number and/or size of uacomments.", strSubVersion.size(), MAX_SUBVERSION_LENGTH)); @@ -2095,6 +2096,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 11: finished SetRPCWarmupFinished(); +#ifdef ENABLE_WEBSOCKETS + ws::SetWebSocketsWarmupFinished(); +#endif uiInterface.InitMessage(_("Done loading")); #ifdef ENABLE_WALLET @@ -2111,7 +2115,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) threadGroup.create_thread(boost::bind(ThreadSendAlert)); if (KOMODO_NSPV_FULLNODE) - fprintf(stderr,"nLocalServices %llx %d, %d\n",(long long)nLocalServices,GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX),GetBoolArg("-spentindex", DEFAULT_SPENTINDEX)); + LogPrintf("nLocalServices %llx %d, %d\n", (long long)nLocalServices, GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX), GetBoolArg("-spentindex", DEFAULT_SPENTINDEX)); return !fRequestShutdown; } diff --git a/src/init.h b/src/init.h index 108339865be..7bef5d37836 100644 --- a/src/init.h +++ b/src/init.h @@ -51,4 +51,6 @@ enum HelpMessageMode { /** Help for options shared between UI and daemon (for -help) */ std::string HelpMessage(HelpMessageMode mode); +extern std::atomic fRequestShutdown; + #endif // BITCOIN_INIT_H diff --git a/src/key_io.cpp b/src/key_io.cpp index dd4176fee12..c51684e7ac6 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -49,6 +49,21 @@ class DestinationEncoder : public boost::static_visitor return EncodeBase58Check(data); } + std::string operator()(const CCryptoConditionID& id) const + { + std::vector data = m_params.Base58Prefix(CChainParams::CRYPTOCONDITION_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } + + std::string operator()(const CCLTVID& id) const + { + if (id.which() == TX_PUBKEY) + return operator()(id.GetPubKey()); + else + return operator()(id.GetKeyID()); + } + std::string operator()(const CNoDestination& no) const { return {}; } }; @@ -72,6 +87,14 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); return CScriptID(hash); } + + // added cc addresses decode: + const std::vector& cc_prefix = params.Base58Prefix(CChainParams::CRYPTOCONDITION_ADDRESS); + if (data.size() == hash.size() + cc_prefix.size() && std::equal(cc_prefix.begin(), cc_prefix.end(), data.begin())) { + std::copy(data.begin() + cc_prefix.size(), data.end(), hash.begin()); + return CCryptoConditionID(hash); + } + } return CNoDestination(); } diff --git a/src/komodo-tx.cpp b/src/komodo-tx.cpp index 21752778454..d25cbc8af42 100644 --- a/src/komodo-tx.cpp +++ b/src/komodo-tx.cpp @@ -41,9 +41,10 @@ using namespace std; #include "uint256.h" #include "arith_uint256.h" +#include "komodo_defs.h" #include "komodo_structs.h" #include "komodo_globals.h" -#include "komodo_defs.h" + #include "komodo_interest.h" diff --git a/src/komodo.h b/src/komodo.h index 88e741df7f7..bfcfb0b789c 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -37,10 +37,7 @@ uint256 NOTARIZED_HASH,NOTARIZED_DESTTXID; #include "uthash.h" #include "utlist.h" -int32_t gettxout_scriptPubKey(uint8_t *scriptPubkey,int32_t maxsize,uint256 txid,int32_t n); -void komodo_event_rewind(struct komodo_state *sp,char *symbol,int32_t height); -int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block); -bool check_pprevnotarizedht(); +//bool check_pprevnotarizedht(); #include "komodo_structs.h" #include "komodo_globals.h" @@ -67,7 +64,6 @@ void komodo_currentheight_set(int32_t height) sp->CURRENT_HEIGHT = height; } -extern struct NSPV_inforesp NSPV_inforesult; int32_t komodo_currentheight() { char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; struct komodo_state *sp; @@ -792,7 +788,7 @@ int32_t komodo_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notar // if txi == 0 && 2 outputs and 2nd OP_RETURN, len == 32*2+4 -> notarized, 1st byte 'P' -> pricefeed // OP_RETURN: 'D' -> deposit, 'W' -> withdraw -int32_t gettxout_scriptPubKey(uint8_t *scriptPubKey,int32_t maxsize,uint256 txid,int32_t n); +//int32_t gettxout_scriptPubKey(uint8_t *scriptPubKey,int32_t maxsize,uint256 txid,int32_t n); int32_t komodo_notarycmp(uint8_t *scriptPubKey,int32_t scriptlen,uint8_t pubkeys[64][33],int32_t numnotaries,uint8_t rmd160[20]) { @@ -820,6 +816,8 @@ int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block) uint64_t signedmask,voutmask; char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; struct komodo_state *sp; uint8_t scriptbuf[10001],pubkeys[64][33],rmd160[20],scriptPubKey[35]; uint256 zero,btctxid,txhash; int32_t i,j,k,numnotaries,notarized,scriptlen,isratification,nid,numvalid,specialtx,notarizedheight,notaryid,len,numvouts,numvins,height,txn_count; + + AssertLockHeld(cs_main); if ( pindex == 0 ) { fprintf(stderr,"komodo_connectblock null pindex\n"); diff --git a/src/komodo_DEX.h b/src/komodo_DEX.h index 4eb4c9ef524..c2711f0af57 100644 --- a/src/komodo_DEX.h +++ b/src/komodo_DEX.h @@ -60,11 +60,6 @@ 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 @@ -171,6 +166,29 @@ static struct DEX_globals FILE *fp; } *G; +void komodo_DEX_privkey(bits256 &privkey) +{ + bits256 priv,hash; + Myprivkey(priv.bytes); + vcalc_sha256(0,hash.bytes,priv.bytes,32); + vcalc_sha256(0,privkey.bytes,hash.bytes,32); + memset(priv.bytes,0,sizeof(priv)); + memset(hash.bytes,0,sizeof(hash)); +} + +void komodo_DEX_pubkey(bits256 &pubkey) +{ + bits256 privkey; + komodo_DEX_privkey(privkey); + /*{ + char *bits256_str(char hexstr[65],bits256 x); + char str[65]; + fprintf(stderr,"new DEX_privkey %s\n",bits256_str(str,privkey)); + }*/ + pubkey = curve25519(privkey,curve25519_basepoint9()); + memset(privkey.bytes,0,sizeof(privkey)); +} + void komodo_DEX_pubkeyupdate() { komodo_DEX_pubkey(DEX_pubkey); @@ -253,6 +271,34 @@ uint32_t komodo_DEXquotehash(bits256 &hash,uint8_t *msg,int32_t len) return(_komodo_DEXquotehash(hash,len)); } +uint8_t *komodo_DEX_encrypt(uint8_t **allocatedp,uint8_t *data,int32_t *datalenp,bits256 destpubkey,bits256 privkey) +{ + int32_t cipherlen; uint8_t *cipher; + cipher = SuperNET_ciphercalc(allocatedp,&cipherlen,privkey,destpubkey,data,*datalenp); + *datalenp = cipherlen; + return(cipher); +} + +uint8_t *komodo_DEX_decrypt(uint8_t *senderpub,uint8_t **allocatedp,uint8_t *data,int32_t *datalenp,bits256 privkey) +{ + int32_t msglen; + *allocatedp = 0; + if ( (msglen= *datalenp) <= crypto_box_NONCEBYTES + crypto_box_ZEROBYTES + sizeof(bits256) ) + { + *datalenp = 0; + return(0); + } + if ( (data= SuperNET_deciphercalc(senderpub,allocatedp,&msglen,privkey,data,*datalenp)) == 0 ) + { + //printf("komodo_DEX_decrypt decrytion error\n"); + *datalenp = 0; + return(0); + } else *datalenp = msglen; + return(data); +} + + + 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() diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 15ab79f0e68..ac8ad798f53 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -19,22 +19,14 @@ #include #include "primitives/nonce.h" #include "consensus/params.h" -#include "komodo_defs.h" #include "script/standard.h" +#include "init.h" +#include "main.h" +#include "komodo_defs.h" #include "cc/CCinclude.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); -int32_t komodo_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryid,uint8_t *scriptbuf,int32_t scriptlen,int32_t height,uint256 txhash,int32_t i,int32_t j,uint64_t *voutmaskp,int32_t *specialtxp,int32_t *notarizedheightp,uint64_t value,int32_t notarized,uint64_t signedmask,uint32_t timestamp); -unsigned int lwmaGetNextPOSRequired(const CBlockIndex* pindexLast, const Consensus::Params& params); -bool EnsureWalletIsAvailable(bool avoidException); -extern bool fRequestShutdown; -extern CScript KOMODO_EARLYTXID_SCRIPTPUB; - -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)) struct MemoryStruct { char *memory; size_t size; }; @@ -527,7 +519,7 @@ int32_t komodo_verifynotarization(char *symbol,char *dest,int32_t height,int32_t { if ( (json= cJSON_Parse(jsonstr)) != 0 ) { - if ( (txjson= jobj(json,(char *)"result")) != 0 && (vouts= jarray(&n,txjson,(char *)"vout")) > 0 ) + if ( (txjson= jobj(json,(char *)"result")) != 0 && (vouts= jarray(&n,txjson,(char *)"vout")) != 0 ) { vout = jitem(vouts,n-1); if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) @@ -942,6 +934,7 @@ int32_t komodo_blockload(CBlock& block,CBlockIndex *pindex) uint32_t komodo_chainactive_timestamp() { + AssertLockHeld(cs_main); if ( chainActive.LastTip() != 0 ) return((uint32_t)chainActive.LastTip()->GetBlockTime()); else return(0); @@ -949,6 +942,7 @@ uint32_t komodo_chainactive_timestamp() CBlockIndex *komodo_chainactive(int32_t height) { + AssertLockHeld(cs_main); if ( chainActive.LastTip() != 0 ) { if ( height <= chainActive.LastTip()->GetHeight() ) @@ -961,6 +955,7 @@ CBlockIndex *komodo_chainactive(int32_t height) uint32_t komodo_heightstamp(int32_t height) { + AssertLockHeld(cs_main); CBlockIndex *ptr; if ( height > 0 && (ptr= komodo_chainactive(height)) != 0 ) return(ptr->nTime); @@ -1268,11 +1263,11 @@ uint32_t komodo_interest_args(uint32_t *txheighttimep,int32_t *txheightp,uint32_ return(locktime); } -uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); - 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) { uint64_t value; uint32_t tiptime=0,txheighttimep; CBlockIndex *pindex; + AssertLockHeld(cs_main); + if ( (pindex= chainActive[tipheight]) != 0 ) tiptime = (uint32_t)pindex->nTime; else fprintf(stderr,"cant find height[%d]\n",tipheight); @@ -1288,15 +1283,25 @@ uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 int32_t komodo_nextheight() { - CBlockIndex *pindex; int32_t ht; - if ( (pindex= chainActive.LastTip()) != 0 && (ht= pindex->GetHeight()) > 0 ) - return(ht+1); - else return(komodo_longestchain() + 1); + CBlockIndex* pindex; + int32_t ht; + + { + LOCK(cs_main); // dimxy added to protect chainActive concurrent use. + pindex = chainActive.LastTip(); + } + + if (pindex != nullptr && (ht = pindex->GetHeight()) > 0) + return (ht + 1); + else + return (komodo_longestchain() + 1); } int32_t komodo_isrealtime(int32_t *kmdheightp) { struct komodo_state *sp; CBlockIndex *pindex; + + AssertLockHeld(cs_main); if ( (sp= komodo_stateptrget((char *)"KMD")) != 0 ) *kmdheightp = sp->CURRENT_HEIGHT; else *kmdheightp = 0; diff --git a/src/komodo_defs.h b/src/komodo_defs.h index 58c8e9d880e..b65b7d07f9d 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -18,7 +18,13 @@ #include #include "arith_uint256.h" +#include "script/script.h" +#include "pubkey.h" #include "chain.h" +#include "primitives/transaction.h" +#include "threadsafety.h" + +#include "komodo_structs.h" #include "komodo_nk.h" #define KOMODO_EARLYTXID_HEIGHT 100 @@ -40,6 +46,10 @@ #define ASSETCHAINS_STAKED_MIN_POW_DIFF 536900000 // 537000000 537300000 #define _COINBASE_MATURITY 100 +#define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff + +#define IS_KMD_CHAIN() (ASSETCHAINS_SYMBOL[0] == '\0') + #define KOMODO_ADDRESS_BUFSIZE 64 // KMD Notary Seasons @@ -50,7 +60,7 @@ // 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 5 +#define NUM_KMD_SEASONS 7 #define NUM_KMD_NOTARIES 64 extern const uint32_t nStakedDecemberHardforkTimestamp; //December 2019 hardfork @@ -59,8 +69,14 @@ extern const int32_t nDecemberHardforkHeight; //December 2019 hardfork 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}; +extern const uint32_t nS5Timestamp; //dPoW Season 5 June 14th, 2021 hardfork (03:00:00 PM UTC) (defined in komodo_globals.h) +extern const int32_t nS5HardforkHeight; //dPoW Season 5 June 14th, 2021 hardfork estimated block height (defined in komodo_globals.h) + +extern const uint32_t nS6Timestamp; // dPoW Season 6 Fri Jun 24 2022 13:37:33 GMT+0000 +extern const int32_t nS6HardforkHeight; // estimated June 24 2022 + +static const uint32_t KMD_SEASON_TIMESTAMPS[NUM_KMD_SEASONS] = {1525132800, 1563148800, nStakedDecemberHardforkTimestamp, nS4Timestamp, nS5Timestamp, nS6Timestamp, 1751328000}; +static const int32_t KMD_SEASON_HEIGHTS[NUM_KMD_SEASONS] = {814000, 1444000, nDecemberHardforkHeight, nS4HardforkHeight, nS5HardforkHeight, nS6HardforkHeight, 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] = @@ -268,7 +284,7 @@ static const char *notaries_elected[NUM_KMD_SEASONS][NUM_KMD_NOTARIES][2] = {"madmax_NA", "0237e0d3268cebfa235958808db1efc20cc43b31100813b1f3e15cc5aa647ad2c3" }, // 0 {"alright_AR", "020566fe2fb3874258b2d3cf1809a5d650e0edc7ba746fa5eec72750c5188c9cc9" }, {"strob_NA", "0206f7a2e972d9dfef1c424c731503a0a27de1ba7a15a91a362dc7ec0d0fb47685" }, - {"hunter_EU", "0378224b4e9d8a0083ce36f2963ec0a4e231ec06b0c780de108e37f41181a89f6a" }, // FIXME verify this, kolo. Change name if you want + {"hunter_EU", "0378224b4e9d8a0083ce36f2963ec0a4e231ec06b0c780de108e37f41181a89f6a" }, {"phm87_SH", "021773a38db1bc3ede7f28142f901a161c7b7737875edbb40082a201c55dcf0add" }, {"chainmakers_NA", "02285d813c30c0bf7eefdab1ff0a8ad08a07a0d26d8b95b3943ce814ac8e24d885" }, {"indenodes_EU", "0221387ff95c44cb52b86552e3ec118a3c311ca65b75bf807c6c07eaeb1be8303c" }, @@ -324,7 +340,7 @@ static const char *notaries_elected[NUM_KMD_SEASONS][NUM_KMD_NOTARIES][2] = {"computergenie_NA", "03a78ae070a5e9e935112cf7ea8293f18950f1011694ea0260799e8762c8a6f0a4" }, {"nutellalicka_SH", "02f7d90d0510c598ce45915e6372a9cd0ba72664cb65ce231f25d526fc3c5479fc" }, {"chainstrike_SH", "03b806be3bf7a1f2f6290ec5c1ea7d3ea57774dcfcf2129a82b2569e585100e1cb" }, - {"hunter_SH", "02407db70ad30ce4dfaee8b4ae35fae88390cad2b0ba0373fdd6231967537ccfdf" }, // FIXME verify this, Decker. Change name if you want + {"hunter_SH", "02407db70ad30ce4dfaee8b4ae35fae88390cad2b0ba0373fdd6231967537ccfdf" }, {"alien_EU", "03bb749e337b9074465fa28e757b5aa92cb1f0fea1a39589bca91a602834d443cd" }, // 60 {"gt_AR", "0348430538a4944d3162bb4749d8c5ed51299c2434f3ee69c11a1f7815b3f46135" }, {"patchkez_SH", "03f45e9beb5c4cd46525db8195eb05c1db84ae7ef3603566b3d775770eba3b96ee" }, @@ -396,7 +412,141 @@ static const char *notaries_elected[NUM_KMD_SEASONS][NUM_KMD_NOTARIES][2] = { "artemii235_DEV", "03bb616b12430bdd0483653de18733597a4fd416623c7065c0e21fe9d96460add1" }, { "tonyl_DEV", "02d5f7fd6e25d34ab2f3318d60cdb89ff3a812ec5d0212c4c113bb12d12616cfdc" }, { "decker_DEV", "028eea44a09674dda00d88ffd199a09c9b75ba9782382cc8f1e97c0fd565fe5707" } - } + }, + { + // Season 5 + {"alrighttt_DEV", "02a876c6c35060041f6beadb201f4dfc567e80eedd3a4206ff10d99878087bd440" }, // 0 + {"alien_AR", "024f20c096b085308e21893383f44b4faf1cdedea9ad53cc7d7e7fbfa0c30c1e71" }, + {"artempikulin_AR", "03a45c4ad7f279cbc50acb48d81fc0eb63c4c5f556e3a4393fb3d6414df09c6e4c" }, + {"chmex_AR", "030cd487e10fbf142e0e8d582e702ecb775f378569c3cb5acd0ff97b6b12803588" }, + {"cipi_AR", "02336758998f474659020e6887ece61ac7b8567f9b2d38724ebf77ae800c1fb2b7" }, + {"shadowbit_AR", "03949b06c2773b4573aeb0b52e70ccc2d98dc5794a47e24eeb902c9d28e0e8d28b" }, + {"goldenman_AR", "03d745bc6921104b73734e6d9615671bc70b9e11e26c9b0c9abf0d2f9babd01a4d" }, + {"kolo_AR", "027579d0722b2f75b3d11a73829449e4251b4471716b6cb743c7667379750c8fb0" }, + {"madmax_AR", "02ddb23f18e61ea792ae0f28be5a52859e7963bf7f1d2c4f19eec18ac6497cfa2a" }, + {"mcrypt_AR", "02845d016c68c3e5ce924b164abc271511f3092ae359677a515e8f81a9533472f4" }, + {"mrlynch_AR", "03e67440141f53a08684c329ebc852b018e41f905da88e52aa4a6dc5aa4b12447a" }, // 10 + {"ocean_AR", "02d216e72d37a38449d661413cbc6e1f008b21cffdb06865f7be636e2cbc1e5346" }, + {"smdmitry_AR", "0397b7584cb29717b721c0c587d4462477efc1f36a56921f133c9d17b0cd7f278a" }, + {"tokel_AR", "02e4e07060fcd3640a3fd6d54cc15924f2bf63f8172b96a9f1d538ca7a0e490dc5" }, + {"tonyl_AR", "02e2d9ecdc9f462a4767f7dfe8ed243c98fcccc1511989a60e3f859dc6fda42d16" }, + {"tonyl_DEV", "0399c4f8c5b604cda64c1ccb8fdbd7a89730131519f87491a79b0811e619102d8f" }, + {"artem_DEV", "025ee88d1c12f546c1c8942d7a3e0678f10bc27cc566e27bf4a2d2178e018d18c6" }, + {"alien_EU", "022b85908191788f409506ebcf96a892f3274f352864c3ed566c5a16de63953236" }, + {"alienx_EU", "025de0911bab55616c307f02ea8a5915a2e0c8e479aa97968e7f00d1025cbe6c6d" }, + {"ca333_EU", "03a582cfae3760bb1cb38311d686cfeede8f8c4ce263aa1c082fc836c367859122" }, + {"chmex_EU", "030bf7bd7ad0515c33b5d5d9a91e0729baf801b9002f80495ae535ea1cebb352cb" }, // 20 + {"cipi_EU", "033a812d6cccdc4208378728f3a0e15db5b12074def9ab686ddc3752715ff1a194" }, + {"cipi2_EU", "0302ca28a041ed00544de737651bdec9bafe3b7f1c0bf2c6092f2368d59fec75c2" }, + {"shadowbit_EU", "025f8de3a6181270ceb5c31654e6a6e95d0339bc14b46b5e3050e8a69861c91baa" }, + {"komodopioneers_EU", "02fb31b130babe79ac780a6118702555a8c66875835f35c2232a6cb8b1438fe71d" }, + {"madmax_EU", "02e7e5306f159df252ecfded9bab6297050d12640b908b456ea553f90872f8a160" }, + {"marmarachain_EU", "027029380f49b0c3cc1b814976f1a83f0c25d84020ad0a27454e55ebdb2ccc83d7" }, + {"node-9_EU", "029401e427cffa29bb2bd7664110e160d525fac6f1518ac7b59343b16de301e0ac" }, + {"slyris_EU", "02a0705ec221a94a6a5b3ea2e763ba0350f8213c73e8dad49a708fb1e87acdc5f8" }, + {"smdmitry_EU", "0338f30ca34d0aca0d79b69abde447036aaaa75f482b6c75801fd382e984337d01" }, + {"van_EU", "0370305b9e91d46331da202ae733d6050d01038ef6eceb2036ada394a48fae84b9" }, // 30 + {"shadowbit_DEV", "03e2de3418c88be0cfe2fa0dcfdaea001b5a36ad86e6833ad284d79021ae7e2b94" }, + {"gcharang_DEV", "0321868e0eb39271330fa2c3a9f4e542275d9719f8b87773c5432448ab10d6943d" }, + {"alien_NA", "022f62b56ddfd07c9860921c701285ac39bb3ac8f6f083d1b59c8f4943be3de162" }, + {"alienx_NA", "025d5e11725233ab161f4f63d697c5f9f0c6b9d3aa2b9c68299638f8cc63faa9c2" }, + {"cipi_NA", "0335352862da521bd90b99d394db1ee3ecde379db9cf7ba2f28b16fa76153e289f" }, + {"computergenie_NA", "02f945d87b7cd6e9f2173a110399d36b369edb1f10bdf5a4ba6fd4923e2986e137" }, + {"dragonhound_NA", "0366a87a476a09e05560c5aae0e44d2ab9ba56e69701cee24307871ddd37c86258" }, + {"hyper_NA", "0303503ea8f5ec8bcab474962dfadd3561b44732b6ad308acd8d04276dd2f1baf3" }, + {"madmax_NA", "0378e47061572e4a406bbad1522c03c3331d0a6c820fde1248ccf2cbc72fec47c2" }, + {"node-9_NA", "03fac1468a949244dd4c563062459d46e966479fe23748382fc2e3e8d05218023e" }, // 40 + {"nodeone_NA", "0310a249c6c2dcc29f2135715138a9ddb8e01c0eab701cbd0b96d9cec660dbdc58" }, + {"pbca26_NA", "03e8485883eba6d4f2902338ab6aac87654a4b98d3bc01f89638aaf9c37db66ccf" }, + {"ptyx_NA", "028267c92db3c48a99dfb8d88e9cdab60d8a1525913ab3978b1b629667b12b1ee2" }, + {"strob_NA", "02285bf2f9e96068ecac14bc6f770e394927b4da9f5ba833eaa9468b5d47f203a3" }, + {"karasugoi_NA", "02f803e6f159824a181cc5d709f3d1e7ff65f19e1899920724aeb4e3d2d869f911" }, + {"webworker01_NA", "03d6c76aabe24fde7ce7cc37cff0899d50a20d4147ac0b2db812e2a1edcf0d5232" }, + {"yurii_DEV", "0243977da0533c7c1a37f0f6e30175225c9012d9f3f426180ff6e5710f5a50e32b" }, + {"ca333_DEV", "035f3413d71856ac0859f564ced42fe1ce5c5058df888f4592b8a11d34a5ba3a45" }, + {"chmex_SH", "03e09c8ee6ae20cde64857d116c4bb5d50db6de2887ac39ea3ccf6434b1abf8698" }, + {"collider_SH", "033a1b62de10c3802f359da7767b033eac3837b58530722f3ddd2f359a2cd0a8f9" }, // 50 + {"dappvader_SH", "02684e2e7425ffa36d331f7a2f9c4542b61e88370dc6b4313a5025643f82ee17fa" }, + {"drkush_SH", "0210320b03f00f10f16313eb6e8929b5be7e66a034a4e9b7d11f2d87aa92708c6c" }, + {"majora31_SH", "03bc75c112ac7c6a99d6eb3fe5582feef4fd1b43f11c08ad887e21c4c3bc4e9104" }, + {"mcrypt_SH", "027a4ca7b11d3456ff558c08bb04483a89c7f383448461fd0b6b3b07424aabe9a4" }, + {"metaphilibert_SH", "03b21ff042bf1730b28bde43f44c064578b41996117ac7634b567c3773089e3be3" }, + {"mylo_SH", "026a52dba25ca4deb225a5ef7fca117d59e20ef2319b00e1bb6750a5d61e5ed601" }, + {"nutellaLicka_SH", "03ca46ea9a32de632823419948188088069f5820023920d810da6076624adb9901" }, + {"pbca26_SH", "021b39173b2b966ab277799a1f148a1d9e6cf26020f5f7eb9708f020ee0461e9c0" }, + {"phit_SH", "021b893b7978284e3d73701a623f23104fcce27e70fb49427c215f9a7481f652da" }, + {"sheeba_SH", "030dd2c3c02cbc5b3c25e3c54ed02c1541951a6f5ecf8adbd353e8d9052d08b8fc" }, // 60 + {"strob_SH", "0213751a1c59d3489ca85b3d62a3d606dcef7f0428aa021c1978ea16fb38a2fad6" }, + {"strobnidan_SH", "033e33ef18effb979437cd202bb87dc32385e16ebd52d6f762d8a3b308d6f89c52" }, + {"dragonhound_DEV", "02b3c168ed4acd96594288cee3114c77de51b6afe1ab6a866887a13a96ee80f33c" } + }, + { + // Season 6 + {"blackice_DEV", "03e2de3418c88be0cfe2fa0dcfdaea001b5a36ad86e6833ad284d79021ae7e2b94"}, + {"blackice_AR", "03949b06c2773b4573aeb0b52e70ccc2d98dc5794a47e24eeb902c9d28e0e8d28b"}, + {"alien_EU", "022b85908191788f409506ebcf96a892f3274f352864c3ed566c5a16de63953236"}, + {"alien_NA", "022f62b56ddfd07c9860921c701285ac39bb3ac8f6f083d1b59c8f4943be3de162"}, + {"alien_SH", "024f20c096b085308e21893383f44b4faf1cdedea9ad53cc7d7e7fbfa0c30c1e71"}, + {"alienx_EU", "025de0911bab55616c307f02ea8a5915a2e0c8e479aa97968e7f00d1025cbe6c6d"}, + {"alienx_NA", "025d5e11725233ab161f4f63d697c5f9f0c6b9d3aa2b9c68299638f8cc63faa9c2"}, + {"artem.pikulin_AR", "03a45c4ad7f279cbc50acb48d81fc0eb63c4c5f556e3a4393fb3d6414df09c6e4c"}, + {"artem.pikulin_DEV", "025ee88d1c12f546c1c8942d7a3e0678f10bc27cc566e27bf4a2d2178e018d18c6"}, + {"blackice_EU", "025f8de3a6181270ceb5c31654e6a6e95d0339bc14b46b5e3050e8a69861c91baa"}, + {"chmex_AR", "030cd487e10fbf142e0e8d582e702ecb775f378569c3cb5acd0ff97b6b12803588"}, + {"chmex_EU", "030bf7bd7ad0515c33b5d5d9a91e0729baf801b9002f80495ae535ea1cebb352cb"}, + {"chmex_NA", "024e88a36d729352a391e07d1821dbfda1fca6409320cf9c2869b6fb99f05fbddd"}, + {"chmex_SH", "03e09c8ee6ae20cde64857d116c4bb5d50db6de2887ac39ea3ccf6434b1abf8698"}, + {"chmex1_SH", "02d59db293de6c7da6673beeb373ebce62fd6d3522f715ea1356b5a2624fbd11a2"}, + {"cipi_1_EU", "033a812d6cccdc4208378728f3a0e15db5b12074def9ab686ddc3752715ff1a194"}, + {"cipi_2_EU", "0302ca28a041ed00544de737651bdec9bafe3b7f1c0bf2c6092f2368d59fec75c2"}, + {"cipi_AR", "02336758998f474659020e6887ece61ac7b8567f9b2d38724ebf77ae800c1fb2b7"}, + {"cipi_NA", "0335352862da521bd90b99d394db1ee3ecde379db9cf7ba2f28b16fa76153e289f"}, + {"computergenie_EU", "033a2474a762700b452b96a49730280040a9872070bc51461e3727f6f118ff5358"}, + {"computergenie_NA", "02f945d87b7cd6e9f2173a110399d36b369edb1f10bdf5a4ba6fd4923e2986e137"}, + {"dimxy_AR", "0337e443df52f278f313f90628aaaa7a8db777f17bc7ce519069eb72717c1c2755"}, + {"dimxy_DEV", "03a7edd6d0ba188960e39eced4d6b4ca6946bd98323ab40cbc13d6e52696de7dc4"}, + {"dragonhound_NA", "0366a87a476a09e05560c5aae0e44d2ab9ba56e69701cee24307871ddd37c86258"}, + {"fediakash_AR", "035be6a54242a53e3ca55bd63430ac9b960fbfaad336d8c1464b5802f03ab184be"}, + {"gcharang_DEV", "03a3878af1152f648e6084fd3fbe697a26b1c2e92d407dd96c375f45f7d3ca13bf"}, + {"gcharang_SH", "0321868e0eb39271330fa2c3a9f4e542275d9719f8b87773c5432448ab10d6943d"}, + {"goldenman_AR", "03e027927dd1e379c2f45624ed9b057b187fe094595fee55c53f36ed665ed566ab"}, + {"kolo_AR", "032c2a6aeaf8176f9630abe2e1bbea5e481da23ab1f9a5b10f220a9b2bc9607bd6"}, + {"kolo_EU", "03b21717e84c0a6cf22b557b198daba4f78980048211b9139b4517fa08f9f17359"}, + {"kolox_AR", "038010e24e6d53f26871d31287f446f407fa87596d946bcd1f7b7b8458e34ad73f"}, + {"komodopioneers_EU", "02fb31b130babe79ac780a6118702555a8c66875835f35c2232a6cb8b1438fe71d"}, + {"madmax_DEV", "02d1ace4a25794de5a5287edeb9df9619dc97020114801c5cb287b1efdd49ddbb3"}, + {"marmarachain_EU", "02ca3e0618bc7c75afa6359ae476ee639682adfaa6fc463bbe7016c4f00da23ccf"}, + {"mcrypt_AR", "02845d016c68c3e5ce924b164abc271511f3092ae359677a515e8f81a9533472f4"}, + {"mcrypt_SH", "027a4ca7b11d3456ff558c08bb04483a89c7f383448461fd0b6b3b07424aabe9a4"}, + {"metaphilibert_SH", "03b21ff042bf1730b28bde43f44c064578b41996117ac7634b567c3773089e3be3"}, + {"mylo_NA", "02493412e6014d2bb985b1026e31204e7634a211a16454fc696f4dc4dfe409e998"}, + {"mylo_SH", "026a52dba25ca4deb225a5ef7fca117d59e20ef2319b00e1bb6750a5d61e5ed601"}, + {"nodeone_NA", "0310a249c6c2dcc29f2135715138a9ddb8e01c0eab701cbd0b96d9cec660dbdc58"}, + {"nutellalicka_AR", "03a5502c61839a34edc8443aee5c16c67d4a958874e13d9b6b69d29075354739ad"}, + {"nutellalicka_SH", "03c7822f0c3434037fa17bfa4d3b7f9d3e0eb9abe49e8cc1188d92c6b3c360a675"}, + {"ocean_AR", "02d216e72d37a38449d661413cbc6e1f008b21cffdb06865f7be636e2cbc1e5346"}, + {"pbca26_NA", "030b6515e80aaa489215875e2aa6f2a15fa36cb3342580e885275f8c636252cbc8"}, + {"pbca26_SH", "02d17840724692a9d5e72d61d001c31eb5ddc4d2f0f104b760e6854bf144e63fb8"}, + {"phit_SH", "021b893b7978284e3d73701a623f23104fcce27e70fb49427c215f9a7481f652da"}, + {"ptyx_NA", "025d72ef0f1c5103de306d536360bcf3c5b6129c2046334b21cec8202e9bfc4baf"}, + {"ptyx2_NA", "02913b3af6f67453eaff2318f3eed0fa0ea60eb2b37c8da64c575fec972f178d9b"}, + {"sheeba_SH", "030dd2c3c02cbc5b3c25e3c54ed02c1541951a6f5ecf8adbd353e8d9052d08b8fc"}, + {"smdmitry_AR", "0397b7584cb29717b721c0c587d4462477efc1f36a56921f133c9d17b0cd7f278a"}, + {"smdmitry_EU", "0338f30ca34d0aca0d79b69abde447036aaaa75f482b6c75801fd382e984337d01"}, + {"smdmitry_SH", "03f7d5ac650baaccedab959adf7c4f416584f4c05a37bf079f710227560c456978"}, + {"strob_SH", "0213751a1c59d3489ca85b3d62a3d606dcef7f0428aa021c1978ea16fb38a2fad6"}, + {"strobnidan_SH", "033e33ef18effb979437cd202bb87dc32385e16ebd52d6f762d8a3b308d6f89c52"}, + {"tokel_NA", "0315d866c09e15709e2036a05faec6aaf28e0ecf67329b2adb922b746f22e694bc"}, + {"tonyl_AR", "02e2d9ecdc9f462a4767f7dfe8ed243c98fcccc1511989a60e3f859dc6fda42d16"}, + {"tonyl_DEV", "0399c4f8c5b604cda64c1ccb8fdbd7a89730131519f87491a79b0811e619102d8f"}, + {"van_EU", "0370305b9e91d46331da202ae733d6050d01038ef6eceb2036ada394a48fae84b9"}, + {"webworker01_EU", "0390ba250f20b5849b9d37ad2bcb58d3d9a3a0385937e652c26511ba9f445f5db1"}, + {"webworker01_NA", "03bde0df98de0ff9697b7d5ecea225fd7267f55c061caa2c43a47b313e35c9b6c6"}, + {"who-biz_NA", "0268d30efafc6ac84b1c8210e99fd4936e178794581d348b87f64fcbbfa8d5e73b"}, + {"yurii-khi_DEV", "0243977da0533c7c1a37f0f6e30175225c9012d9f3f426180ff6e5710f5a50e32b"}, + {"ca333_EU", "03c34a62c8c89889e4529962578e0b75a010a6e1d9bcbe8f4bb9cc680d82c7261e"}, + {"dragonhound_DEV", "02b3c168ed4acd96594288cee3114c77de51b6afe1ab6a866887a13a96ee80f33c"}, + }, }; #define SETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] |= (1 << ((bitoffset) & 7))) @@ -413,7 +563,7 @@ static const char *notaries_elected[NUM_KMD_SEASONS][NUM_KMD_NOTARIES][2] = #define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) #endif -extern uint8_t ASSETCHAINS_TXPOW,ASSETCHAINS_PUBLIC; +extern uint8_t ASSETCHAINS_TXPOW; extern int8_t ASSETCHAINS_ADAPTIVEPOW; int32_t MAX_BLOCK_SIZE(int32_t height); extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; @@ -434,7 +584,10 @@ 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]; -//extern std::vector ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS; +extern std::vector ASSETCHAINS_PRICES, ASSETCHAINS_STOCKS; +extern std::vector Mineropret; // opreturn data set by the data gathering code +extern uint64_t ASSETCHAINS_PEGSCCPARAMS[3]; +extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEYHASH[]; extern int32_t VERUS_BLOCK_POSUNITS, VERUS_CONSECUTIVE_POS_THRESHOLD, VERUS_NOPOS_THRESHHOLD; extern uint256 KOMODO_EARLYTXID; @@ -463,7 +616,6 @@ void komodo_netevent(std::vector payload); int32_t getacseason(uint32_t timestamp); int32_t getkmdseason(int32_t height); -#define IGUANA_MAXSCRIPTSIZE 10001 #define KOMODO_KVDURATION 1440 #define KOMODO_KVBINARY 2 #define PRICES_SMOOTHWIDTH 1 @@ -473,10 +625,8 @@ int32_t komodo_paxprices(int32_t *heights,uint64_t *prices,int32_t max,char *bas int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,int32_t len); int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width); -int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen); uint32_t komodo_blocktime(uint256 hash); -int32_t komodo_longestchain(); int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); int8_t komodo_segid(int32_t nocache,int32_t height); int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); @@ -485,7 +635,6 @@ int32_t komodo_priceind(const char *symbol); int32_t komodo_pricesinit(); 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); int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblocks); @@ -509,6 +658,88 @@ bool komodo_txnotarizedconfirmed(uint256 txid,int32_t minconfirms=1); int32_t komodo_blockload(CBlock& block, CBlockIndex *pindex); uint32_t komodo_chainactive_timestamp(); uint32_t GetLatestTimestamp(int32_t height); +int32_t komodo_get_current_height(); +struct komodo_state *komodo_stateptrget(char *base); + +void komodo_prefetch(FILE *fp); +void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotaries,uint8_t notaryid,uint256 txhash,uint64_t voutmask,uint8_t numvouts,uint32_t *pvals,uint8_t numpvals,int32_t kheight,uint32_t ktime,uint64_t opretvalue,uint8_t *opretbuf,uint16_t opretlen,uint16_t vout,uint256 MoM,int32_t MoMdepth); +void komodo_init(int32_t height); +int32_t komodo_MoMdata(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip); +int32_t komodo_notarizeddata(int32_t nHeight,uint256 *notarized_hashp,uint256 *notarized_desttxidp); +char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port); +int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp); +int32_t komodo_isrealtime(int32_t *kmdheightp); +uint64_t komodo_paxtotal(); +int32_t komodo_longestchain(); +uint64_t komodo_maxallowed(int32_t baseid); +int32_t komodo_bannedset(int32_t *indallvoutsp,uint256 *array,int32_t max); +int32_t komodo_checkvout(int32_t vout,int32_t k,int32_t indallvouts); +int32_t komodo_notarized_height(int32_t *prevMoMheightp,uint256 *hashp,uint256 *txidp); +int64_t komodo_block_unlocktime(uint32_t nHeight); +int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33, void *pTr); +uint64_t komodo_notarypayamount(int32_t nHeight, int64_t notarycount); +CScript komodo_mineropret(int32_t nHeight); +bool komodo_appendACscriptpub(); +uint64_t komodo_commission(const CBlock *pblock,int32_t height); +int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig, uint256 merkleroot); +uint256 komodo_calcmerkleroot(CBlock *pblock, uint256 prevBlockHash, int32_t nHeight, bool fNew, CScript scriptPubKey); +int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey &pk); +int32_t komodo_getnotarizedheight(uint32_t timestamp,int32_t height, uint8_t *script, int32_t len); +int32_t komodo_is_notarytx(const CTransaction& tx); +int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_t cmptime,int32_t dispflag); +uint64_t komodo_notarypay(CMutableTransaction &txNew, std::vector &NotarisationNotaries, uint32_t timestamp, int32_t height, uint8_t *script, int32_t len); +CScript komodo_makeopret(CBlock *pblock, bool fNew); +int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *kmdnotarized_heightp); +int32_t komodo_whoami(char *pubkeystr,int32_t height,uint32_t timestamp); +int64_t komodo_coinsupply(int64_t *zfundsp,int64_t *sproutfundsp,int32_t height); +int32_t komodo_isnotaryvout(char *coinaddr,uint32_t tiptime); +void komodo_passport_iteration(); +void komodo_cbopretupdate(int32_t forceflag); +uint64_t komodo_interestsum(); +uint64_t komodo_interest(int32_t txheight, uint64_t nValue, uint32_t nLockTime, uint32_t tiptime); +bool komodo_dailysnapshot(int32_t height); +void komodo_setactivation(int32_t height); +void komodo_pricesupdate(int32_t height,CBlock *pblock); +void komodo_broadcast(CBlock *pblock,int32_t limit); +int32_t komodo_block2pubkey33(uint8_t *pubkey33,CBlock *block); +void komodo_event_rewind(struct komodo_state *sp,char *symbol,int32_t height); +int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main) ; +arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc,int32_t newStakerActive); +int32_t komodo_baseid(char *origbase); +int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,uint32_t *blocktimes,int32_t *nonzpkeysp,int32_t height); +uint64_t komodo_current_supply(uint32_t nHeight); + +int32_t gettxout_scriptPubKey(uint8_t *scriptPubkey,int32_t maxsize,uint256 txid,int32_t n); +bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey); + +char *nonportable_path(char *str); +char *portable_path(char *str); +void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep); +void *filestr(long *allocsizep,char *_fname); + +extern uint64_t KOMODO_INTERESTSUM, KOMODO_WALLETBALANCE; +extern int32_t KOMODO_INSYNC, KOMODO_LASTMINED, prevKOMODO_LASTMINED; +extern uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS + 1], ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS + 1]; +extern uint64_t ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS + 1]; +extern uint64_t ASSETCHAINS_LINEAR, ASSETCHAINS_SUPPLY; +extern uint8_t ASSETCHAINS_PUBLIC, ASSETCHAINS_PRIVATE; +extern int32_t KOMODO_LOADINGBLOCKS; +extern int32_t ASSETCHAINS_FOUNDERS; +extern char ASSETCHAINS_USERPASS[]; +extern int32_t KOMODO_REWIND; +extern uint32_t ASSETCHAINS_NUMALGOS; +extern uint32_t STAKING_MIN_DIFF; +extern uint32_t ASSETCHAINS_MINDIFF[]; +extern uint64_t ASSETCHAINS_TIMEUNLOCKFROM; +extern uint64_t ASSETCHAINS_TIMEUNLOCKTO; +extern uint32_t KOMODO_DPOWCONFS; +extern uint16_t KMD_PORT, BITCOIND_RPCPORT, DEST_PORT; +extern char KMDUSERPASS[], BTCUSERPASS[]; +extern uint32_t KOMODO_STOPAT; +extern int32_t ASSETCHAINS_CBMATURITY; +extern struct komodo_state KOMODO_STATES[]; +extern int32_t KOMODO_EXTRASATOSHI; + #ifndef KOMODO_NSPV_FULLNODE #define KOMODO_NSPV_FULLNODE (KOMODO_NSPV <= 0) @@ -531,8 +762,77 @@ struct komodo_staking *komodo_addutxo(struct komodo_staking *array, int32_t *num void komodo_createminerstransactions(); uint32_t komodo_segid32(char *coinaddr); -#ifndef _WIN32 -void OS_randombytes(unsigned char *x, long xlen); -#endif +int32_t komodo_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryid,uint8_t *scriptbuf,int32_t scriptlen,int32_t height,uint256 txhash,int32_t i,int32_t j,uint64_t *voutmaskp,int32_t *specialtxp,int32_t *notarizedheightp,uint64_t value,int32_t notarized,uint64_t signedmask,uint32_t timestamp); + + +// #ifndef _WIN32 +void OS_randombytes(unsigned char *x, long xlen); // this func impl exists for win too +// #endif + +// curve25519 and sha256 +#ifndef _BITS256 +#define _BITS256 + union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; + typedef union _bits256 bits256; +#endif + +bits256 curve25519_shared(bits256 privkey,bits256 otherpub); +bits256 curve25519_basepoint9(); +bits256 curve25519(bits256 mysecret,bits256 basepoint); +void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len); +bits256 bits256_doublesha256(char *deprecated,uint8_t *data,int32_t datalen); + +// supernet cipher +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); +uint8_t *SuperNET_deciphercalc(uint8_t *senderpub,uint8_t **ptrp,int32_t *msglenp,bits256 privkey,uint8_t *cipher,int32_t cipherlen); +uint8_t *SuperNET_ciphercalc(uint8_t **ptrp,int32_t *cipherlenp,bits256 privkey,bits256 destpubkey,uint8_t *data,int32_t datalen); + +#ifdef TESTMODE + #define MIN_NON_NOTARIZED_CONFIRMS 2 +#else + #define MIN_NON_NOTARIZED_CONFIRMS 101 +#endif // TESTMODE + + +extern int32_t JUMBLR_PAUSE; +int32_t Jumblr_depositaddradd(char *depositaddr); +int32_t Jumblr_secretaddradd(char *secretaddr); + + +#define KOMODO_KVPROTECTED 1 +#define KOMODO_KVBINARY 2 +#define KOMODO_KVDURATION 1440 + +uint64_t PAX_fiatdest(uint64_t *seedp,int32_t tokomodo,char *destaddr,uint8_t pubkey37[37],char *coinaddr,int32_t height,char *base,int64_t fiatoshis); +int32_t komodo_opreturnscript(uint8_t *script,uint8_t type,uint8_t *opret,int32_t opretlen); +#define CRYPTO777_KMDADDR "RXL3YXG2ceaB6C5hfJcN4fvmLH2C34knhA" +extern int32_t KOMODO_PAX; +int32_t komodo_is_issuer(); + +// script tools analogue +#define IGUANA_READ 0 +#define IGUANA_WRITE 1 +int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); +int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *endianedp); +int32_t iguana_rwvarint(int32_t rwflag, uint8_t* serialized, uint64_t* varint64p); +int32_t iguana_rwbuf(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *buf); + +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 komodo_pending_withdraws(char *opretstr); +void komodo_kvupdate(uint8_t *opretbuf,int32_t opretlen,uint64_t value); +int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen); +int32_t komodo_kvcmp(uint8_t *refvalue,uint16_t refvaluesize,uint8_t *value,uint16_t valuesize); +uint64_t komodo_kvfee(uint32_t flags,int32_t opretlen,int32_t keylen); +uint256 komodo_kvsig(uint8_t *buf,int32_t len,uint256 privkey); +int32_t komodo_kvduration(uint32_t flags); +uint256 komodo_kvprivkey(uint256 *pubkeyp,char *passphrase); +int32_t komodo_kvsigverify(uint8_t *buf,int32_t len,uint256 _pubkey,uint256 sig); + +uint64_t komodo_interestnew(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); +uint64_t _komodo_interestnew(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); + +uint32_t komodo_next_tx_locktime(); #endif diff --git a/src/komodo_events.h b/src/komodo_events.h index 5500a341eb2..2afb0e1c989 100644 --- a/src/komodo_events.h +++ b/src/komodo_events.h @@ -103,8 +103,8 @@ void komodo_eventadd_opreturn(struct komodo_state *sp,char *symbol,int32_t heigh O.oplen = (int32_t)(opretlen + sizeof(O)); komodo_eventadd(sp,height,symbol,KOMODO_EVENT_OPRETURN,opret,O.oplen); free(opret); - if ( sp != 0 ) - komodo_opreturn(height,value,buf,opretlen,txid,vout,symbol); + //if ( sp != 0 ) + // komodo_opreturn(height,value,buf,opretlen,txid,vout,symbol); } } diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index de72ab0d30f..b422f9fa8c1 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -682,8 +682,6 @@ int32_t komodo_bannedset(int32_t *indallvoutsp,uint256 *array,int32_t max) return(i); } -void komodo_passport_iteration(); - int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtime) // verify above block is valid pax pricing { static uint256 array[64]; static int32_t numbanned,indallvouts; @@ -1426,8 +1424,6 @@ int32_t komodo_faststateinit(struct komodo_state *sp,char *fname,char *symbol,ch 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; @@ -1582,7 +1578,6 @@ void komodo_passport_iteration() } -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 diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 564be0bf4e1..9c366153918 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -14,22 +14,7 @@ ******************************************************************************/ #include "komodo_defs.h" - -void komodo_prefetch(FILE *fp); -uint32_t komodo_heightstamp(int32_t height); -void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotaries,uint8_t notaryid,uint256 txhash,uint64_t voutmask,uint8_t numvouts,uint32_t *pvals,uint8_t numpvals,int32_t kheight,uint32_t ktime,uint64_t opretvalue,uint8_t *opretbuf,uint16_t opretlen,uint16_t vout,uint256 MoM,int32_t MoMdepth); -void komodo_init(int32_t height); -int32_t komodo_MoMdata(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip); -int32_t komodo_notarizeddata(int32_t nHeight,uint256 *notarized_hashp,uint256 *notarized_desttxidp); -char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port); -void komodo_init(int32_t height); -int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp); -int32_t komodo_isrealtime(int32_t *kmdheightp); -uint64_t komodo_paxtotal(); -int32_t komodo_longestchain(); -uint64_t komodo_maxallowed(int32_t baseid); -int32_t komodo_bannedset(int32_t *indallvoutsp,uint256 *array,int32_t max); -int32_t komodo_checkvout(int32_t vout,int32_t k,int32_t indallvouts); +#include "komodo_structs.h" pthread_mutex_t komodo_mutex,staked_mutex; @@ -47,6 +32,12 @@ 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 +const uint32_t nS5Timestamp = 1627466969; //dPoW Season 5 Wed Jul 28 2021 10:09:29 GMT+0000 (timestamp of TOKEL block #1) +const int32_t nS5HardforkHeight = 2437300; //dPoW Season 5 Monday, June 14th, 2021 + +const uint32_t nS6Timestamp = 1656077853; // dPoW Season 6 Fri Jun 24 2022 13:37:33 GMT+0000 +const int32_t nS6HardforkHeight = 2963330; // estimated June 24 2022 + #define _COINBASE_MATURITY 100 int COINBASE_MATURITY = _COINBASE_MATURITY;//100; unsigned int WITNESS_CACHE_SIZE = _COINBASE_MATURITY+10; @@ -76,14 +67,13 @@ int64_t MAX_MONEY = 200000000 * 100000000LL; // to be verifiable, timelocks require additional data that enables them to be validated and their ownership and // release time determined from the blockchain. to do this, every time locked output according to this // spec will use an op_return with CLTV at front and anything after |OP_RETURN|PUSH of rest|OPRETTYPE_TIMELOCK|script| -#define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff uint64_t ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF; uint64_t ASSETCHAINS_TIMEUNLOCKFROM = 0, ASSETCHAINS_TIMEUNLOCKTO = 0,ASSETCHAINS_CBOPRET=0; 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; +std::vector ASSETCHAINS_PRICES, ASSETCHAINS_STOCKS; #define _ASSETCHAINS_EQUIHASH 0 uint32_t ASSETCHAINS_NUMALGOS = 3; @@ -113,9 +103,9 @@ int32_t ASSETCHAINS_STAKED; uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY = 10,ASSETCHAINS_FOUNDERS_REWARD; uint32_t KOMODO_INITDONE; -char KMDUSERPASS[8192+512+1],BTCUSERPASS[8192]; uint16_t KMD_PORT = 7771,BITCOIND_RPCPORT = 7771, DEST_PORT; +char KMDUSERPASS[8192+512+1],BTCUSERPASS[8192]; +uint16_t KMD_PORT = 7771,BITCOIND_RPCPORT = 7771, DEST_PORT; uint64_t PENDING_KOMODO_TX; -extern int32_t KOMODO_LOADINGBLOCKS; unsigned int MAX_BLOCK_SIGOPS = 20000; int32_t KOMODO_TESTNODE, KOMODO_SNAPSHOT_INTERVAL; diff --git a/src/komodo_nSPV.h b/src/komodo_nSPV.h index 8b6b74fd6c3..6f06a895011 100644 --- a/src/komodo_nSPV.h +++ b/src/komodo_nSPV.h @@ -26,114 +26,186 @@ #ifndef KOMODO_NSPV_H #define KOMODO_NSPV_H +#include +#include "main.h" +#include "komodo_defs.h" +#include "cc/CCinclude.h" +#include "komodo_nSPV_defs.h" + int32_t iguana_rwbuf(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *buf) { - if ( rwflag != 0 ) - memcpy(serialized,buf,len); - else memcpy(buf,serialized,len); - return(len); + if (rwflag != 0) + memcpy(serialized, buf, len); + else + memcpy(buf, serialized, len); + return (len); +} + +int32_t iguana_varint_size(uint64_t varint64) +{ + if (varint64 < 253) + return 1; + else if (varint64 <= 0xFFFFu) + return 3; + else if (varint64 <= 0xFFFFFFFFu) + return 5; + else + return 9; +} + +int32_t iguana_rwvarint(int32_t rwflag, uint8_t* serialized, uint64_t* varint64p) +{ + if (rwflag != IGUANA_READ) { + if (*varint64p < 253) { + serialized[0]= *varint64p; // this byte contains length 1..252 + return 1; + } else if (*varint64p <= 0xFFFFu) { + uint16_t os_var = htole16(*varint64p); + serialized[0] = 253; // next two bytes contain var length + memcpy(serialized+1, (uint8_t*)&os_var, 2); + return 3; + } else if (*varint64p <= 0xFFFFFFFFu) { + uint32_t os_var = htole32(*varint64p); + serialized[0] = 254; // next four bytes contain var length + memcpy(serialized+1, (uint8_t*)&os_var, 4); + return 5; + } else { + uint64_t os_var = htole64(*varint64p); + serialized[0] = 255; // next eight bytes contain var length + memcpy(serialized+1, (uint8_t*)&os_var, 8); + return 9; + } + } else { + if (serialized[0] < 253) { + *varint64p = serialized[0]; // this byte contains length 1..253 + return 1; + } else if (serialized[0] == 253) { + uint16_t net_var; + memcpy((uint8_t*)&net_var, serialized+1, 2); + *varint64p = le16toh(net_var); + return 3; + } else if (serialized[0] == 254) { + uint32_t net_var; + memcpy((uint8_t*)&net_var, serialized+1, 4); + *varint64p = le32toh(net_var); + return 5; + } else { + uint64_t net_var; + memcpy((uint8_t*)&net_var, serialized+1, 8); + *varint64p = le64toh(net_var); + return 9; + } + } } -int32_t NSPV_rwequihdr(int32_t rwflag,uint8_t *serialized,struct NSPV_equihdr *ptr) +int32_t NSPV_rwequihdr(int32_t rwflag, uint8_t* serialized, struct NSPV_equihdr* ptr) { int32_t len = 0; - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nVersion),&ptr->nVersion); - len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashPrevBlock),(uint8_t *)&ptr->hashPrevBlock); - len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashMerkleRoot),(uint8_t *)&ptr->hashMerkleRoot); - len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashFinalSaplingRoot),(uint8_t *)&ptr->hashFinalSaplingRoot); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nTime),&ptr->nTime); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nBits),&ptr->nBits); - len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->nNonce),(uint8_t *)&ptr->nNonce); - len += iguana_rwbuf(rwflag,&serialized[len],sizeof(ptr->nSolution),ptr->nSolution); - return(len); -} - -int32_t iguana_rwequihdrvec(int32_t rwflag,uint8_t *serialized,uint16_t *vecsizep,struct NSPV_equihdr **ptrp) -{ - int32_t i,vsize,len = 0; - len += iguana_rwnum(rwflag,&serialized[len],sizeof(*vecsizep),vecsizep); - if ( (vsize= *vecsizep) != 0 ) - { - //fprintf(stderr,"vsize.%d ptrp.%p alloc %ld\n",vsize,*ptrp,sizeof(struct NSPV_equihdr)*vsize); - if ( *ptrp == 0 ) - *ptrp = (struct NSPV_equihdr *)calloc(sizeof(struct NSPV_equihdr),vsize); // relies on uint16_t being "small" to prevent mem exhaustion - for (i=0; inVersion), &ptr->nVersion); + len += iguana_rwbignum(rwflag, &serialized[len], sizeof(ptr->hashPrevBlock), (uint8_t*)&ptr->hashPrevBlock); + len += iguana_rwbignum(rwflag, &serialized[len], sizeof(ptr->hashMerkleRoot), (uint8_t*)&ptr->hashMerkleRoot); + len += iguana_rwbignum(rwflag, &serialized[len], sizeof(ptr->hashFinalSaplingRoot), (uint8_t*)&ptr->hashFinalSaplingRoot); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->nTime), &ptr->nTime); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->nBits), &ptr->nBits); + len += iguana_rwbignum(rwflag, &serialized[len], sizeof(ptr->nNonce), (uint8_t*)&ptr->nNonce); + len += iguana_rwvarint(rwflag, &serialized[len], &ptr->nSolutionLen); + len += iguana_rwbuf(rwflag, &serialized[len], ptr->nSolutionLen, ptr->nSolution); + return (len); +} + +int32_t iguana_rwequihdrvec(int32_t rwflag, uint8_t* serialized, uint16_t* vecsizep, struct NSPV_equihdr** hdrspp) +{ + int32_t vsize, len = 0; + len += iguana_rwnum(rwflag, &serialized[len], sizeof(*vecsizep), vecsizep); + if ((vsize = *vecsizep) != 0) { + //fprintf(stderr,"vsize.%d hdrspp.%p alloc %ld\n",vsize,*hdrspp,sizeof(struct NSPV_equihdr)*vsize); + if (*hdrspp == nullptr) + *hdrspp = (struct NSPV_equihdr*)calloc(sizeof(struct NSPV_equihdr), vsize); // relies on uint16_t being "small" to prevent mem exhaustion + for (int32_t i = 0; i < vsize; i++) + len += NSPV_rwequihdr(rwflag, &serialized[len], &(*hdrspp)[i]); } - return(len); + return (len); } -int32_t iguana_rwuint8vec(int32_t rwflag,uint8_t *serialized,int32_t *biglenp,uint8_t **ptrp) +int32_t iguana_rwuint8vec(int32_t rwflag, uint8_t* serialized, int32_t* biglenp, uint8_t** ptrp) { - int32_t vsize,len = 0; - len += iguana_rwnum(rwflag,&serialized[len],sizeof(*biglenp),biglenp); - if ( (vsize= *biglenp) > 0 && vsize < MAX_TX_SIZE_AFTER_SAPLING ) - { - if ( *ptrp == 0 ) - *ptrp = (uint8_t *)calloc(1,vsize); - len += iguana_rwbuf(rwflag,&serialized[len],vsize,*ptrp); + int32_t vsize, len = 0; + len += iguana_rwnum(rwflag, &serialized[len], sizeof(*biglenp), biglenp); + if ((vsize = *biglenp) > 0 && vsize < MAX_TX_SIZE_AFTER_SAPLING) { + if (*ptrp == nullptr) + *ptrp = (uint8_t*)calloc(1, vsize); + len += iguana_rwbuf(rwflag, &serialized[len], vsize, *ptrp); } - return(len); + return (len); } -int32_t NSPV_rwutxoresp(int32_t rwflag,uint8_t *serialized,struct NSPV_utxoresp *ptr) +int32_t NSPV_rwutxoresp(int32_t rwflag, uint8_t* serialized, struct NSPV_utxoresp* ptr) { int32_t len = 0; - len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->satoshis),&ptr->satoshis); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->extradata),&ptr->extradata); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->vout),&ptr->vout); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); - return(len); + len += iguana_rwbignum(rwflag, &serialized[len], sizeof(ptr->txid), (uint8_t*)&ptr->txid); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->satoshis), &ptr->satoshis); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->extradata), &ptr->extradata); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->vout), &ptr->vout); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->height), &ptr->height); + len += iguana_rwvarint(rwflag, &serialized[len], &ptr->script_size); + len += iguana_rwbuf(rwflag, &serialized[len], ptr->script_size, ptr->script); + return (len); } -int32_t NSPV_rwutxosresp(int32_t rwflag,uint8_t *serialized,struct NSPV_utxosresp *ptr) // check mempool +int32_t NSPV_rwutxosresp(int32_t rwflag, uint8_t* serialized, struct NSPV_utxosresp* ptr) // check mempool { - int32_t i,len = 0; - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->numutxos),&ptr->numutxos); - if ( ptr->numutxos != 0 ) - { - if ( ptr->utxos == 0 ) - ptr->utxos = (struct NSPV_utxoresp *)calloc(sizeof(*ptr->utxos),ptr->numutxos); // relies on uint16_t being "small" to prevent mem exhaustion - for (i=0; inumutxos; i++) - len += NSPV_rwutxoresp(rwflag,&serialized[len],&ptr->utxos[i]); + int32_t len = 0; + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->numutxos), &ptr->numutxos); + if (ptr->numutxos != 0) { + if (ptr->utxos == nullptr) + ptr->utxos = (struct NSPV_utxoresp*)calloc(sizeof(*ptr->utxos), ptr->numutxos); // relies on uint16_t being "small" to prevent mem exhaustion + for (int32_t i = 0; i < ptr->numutxos; i++) + len += NSPV_rwutxoresp(rwflag, &serialized[len], &ptr->utxos[i]); } - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->total),&ptr->total); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->interest),&ptr->interest); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nodeheight),&ptr->nodeheight); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->filter),&ptr->filter); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->CCflag),&ptr->CCflag); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->skipcount),&ptr->skipcount); - if ( rwflag != 0 ) - { - memcpy(&serialized[len],ptr->coinaddr,sizeof(ptr->coinaddr)); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->total), &ptr->total); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->interest), &ptr->interest); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->nodeheight), &ptr->nodeheight); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->maxrecords), &ptr->maxrecords); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->CCflag), &ptr->CCflag); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->skipcount), &ptr->skipcount); + if (rwflag != 0) { + memcpy(&serialized[len], ptr->coinaddr, sizeof(ptr->coinaddr)); len += sizeof(ptr->coinaddr); - } - else - { - memcpy(ptr->coinaddr,&serialized[len],sizeof(ptr->coinaddr)); + } else { + memcpy(ptr->coinaddr, &serialized[len], sizeof(ptr->coinaddr)); len += sizeof(ptr->coinaddr); } - return(len); + return (len); } void NSPV_utxosresp_purge(struct NSPV_utxosresp *ptr) { - if ( ptr != 0 ) - { - if ( ptr->utxos != 0 ) + if (ptr != nullptr) { + if (ptr->utxos != nullptr) { + for(size_t i = 0; i < ptr->numutxos; i ++) + if (ptr->utxos[i].script != nullptr) + free(ptr->utxos[i].script); free(ptr->utxos); - memset(ptr,0,sizeof(*ptr)); + } + memset(ptr, 0, sizeof(*ptr)); } } void NSPV_utxosresp_copy(struct NSPV_utxosresp *dest,struct NSPV_utxosresp *ptr) { *dest = *ptr; - if ( ptr->utxos != 0 ) - { - dest->utxos = (struct NSPV_utxoresp *)malloc(ptr->numutxos * sizeof(*ptr->utxos)); - memcpy(dest->utxos,ptr->utxos,ptr->numutxos * sizeof(*ptr->utxos)); + if (ptr->utxos != 0) { + dest->utxos = (struct NSPV_utxoresp*)malloc(ptr->numutxos * sizeof(*ptr->utxos)); + memcpy(dest->utxos, ptr->utxos, ptr->numutxos * sizeof(*ptr->utxos)); + for (size_t i = 0; i < ptr->numutxos; i++) { + dest->utxos[i].script = NULL; + dest->utxos[i].script_size = 0; + if (ptr->utxos[i].script) { + dest->utxos->script = (uint8_t*)malloc(ptr->utxos[i].script_size); + memcpy(dest->utxos[i].script, ptr->utxos[i].script, ptr->utxos[i].script_size); + dest->utxos[i].script_size = ptr->utxos[i].script_size; + } + } } } @@ -142,7 +214,7 @@ int32_t NSPV_rwtxidresp(int32_t rwflag,uint8_t *serialized,struct NSPV_txidresp int32_t len = 0; len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->satoshis),&ptr->satoshis); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->vout),&ptr->vout); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->index),&ptr->index); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); return(len); } @@ -153,13 +225,13 @@ int32_t NSPV_rwtxidsresp(int32_t rwflag,uint8_t *serialized,struct NSPV_txidsres len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->numtxids),&ptr->numtxids); if ( ptr->numtxids != 0 ) { - if ( ptr->txids == 0 ) + if ( ptr->txids == nullptr ) ptr->txids = (struct NSPV_txidresp *)calloc(sizeof(*ptr->txids),ptr->numtxids); for (i=0; inumtxids; i++) len += NSPV_rwtxidresp(rwflag,&serialized[len],&ptr->txids[i]); } len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nodeheight),&ptr->nodeheight); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->filter),&ptr->filter); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->maxrecords),&ptr->maxrecords); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->CCflag),&ptr->CCflag); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->skipcount),&ptr->skipcount); if ( rwflag != 0 ) @@ -178,21 +250,20 @@ int32_t NSPV_rwtxidsresp(int32_t rwflag,uint8_t *serialized,struct NSPV_txidsres void NSPV_txidsresp_purge(struct NSPV_txidsresp *ptr) { - if ( ptr != 0 ) - { - if ( ptr->txids != 0 ) + if (ptr != nullptr) { + if (ptr->txids != nullptr) { free(ptr->txids); - memset(ptr,0,sizeof(*ptr)); + } + memset(ptr, 0, sizeof(*ptr)); } } void NSPV_txidsresp_copy(struct NSPV_txidsresp *dest,struct NSPV_txidsresp *ptr) { *dest = *ptr; - if ( ptr->txids != 0 ) - { - dest->txids = (struct NSPV_txidresp *)malloc(ptr->numtxids * sizeof(*ptr->txids)); - memcpy(dest->txids,ptr->txids,ptr->numtxids * sizeof(*ptr->txids)); + if (ptr->txids != 0) { + dest->txids = (struct NSPV_txidresp*)malloc(ptr->numtxids * sizeof(*ptr->txids)); + memcpy(dest->txids, ptr->txids, ptr->numtxids * sizeof(*ptr->txids)); } } @@ -202,7 +273,7 @@ int32_t NSPV_rwmempoolresp(int32_t rwflag,uint8_t *serialized,struct NSPV_mempoo len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->numtxids),&ptr->numtxids); if ( ptr->numtxids != 0 ) { - if ( ptr->txids == 0 ) + if ( ptr->txids == nullptr ) ptr->txids = (uint256 *)calloc(sizeof(*ptr->txids),ptr->numtxids); for (i=0; inumtxids; i++) len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txids[i]),(uint8_t *)&ptr->txids[i]); @@ -229,9 +300,9 @@ int32_t NSPV_rwmempoolresp(int32_t rwflag,uint8_t *serialized,struct NSPV_mempoo void NSPV_mempoolresp_purge(struct NSPV_mempoolresp *ptr) { - if ( ptr != 0 ) + if ( ptr != nullptr ) { - if ( ptr->txids != 0 ) + if ( ptr->txids != nullptr ) free(ptr->txids); memset(ptr,0,sizeof(*ptr)); } @@ -240,30 +311,30 @@ void NSPV_mempoolresp_purge(struct NSPV_mempoolresp *ptr) void NSPV_mempoolresp_copy(struct NSPV_mempoolresp *dest,struct NSPV_mempoolresp *ptr) { *dest = *ptr; - if ( ptr->txids != 0 ) + if (ptr->txids != nullptr) { dest->txids = (uint256 *)malloc(ptr->numtxids * sizeof(*ptr->txids)); memcpy(dest->txids,ptr->txids,ptr->numtxids * sizeof(*ptr->txids)); } } -int32_t NSPV_rwntz(int32_t rwflag,uint8_t *serialized,struct NSPV_ntz *ptr) +int32_t NSPV_rwntz(int32_t rwflag, uint8_t* serialized, struct NSPV_ntz* ptr) { int32_t len = 0; - len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->blockhash),(uint8_t *)&ptr->blockhash); - len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); - len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->othertxid),(uint8_t *)&ptr->othertxid); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->txidheight),&ptr->txidheight); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->timestamp),&ptr->timestamp); - return(len); + len += iguana_rwbignum(rwflag, &serialized[len], sizeof(ptr->ntzblockhash), (uint8_t*)&ptr->ntzblockhash); + len += iguana_rwbignum(rwflag, &serialized[len], sizeof(ptr->txid), (uint8_t*)&ptr->txid); + len += iguana_rwbignum(rwflag, &serialized[len], sizeof(ptr->desttxid), (uint8_t*)&ptr->desttxid); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->ntzheight), &ptr->ntzheight); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->txidheight), &ptr->txidheight); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->timestamp), &ptr->timestamp); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->depth), &ptr->depth); + return (len); } int32_t NSPV_rwntzsresp(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzsresp *ptr) { int32_t len = 0; - len += NSPV_rwntz(rwflag,&serialized[len],&ptr->prevntz); - len += NSPV_rwntz(rwflag,&serialized[len],&ptr->nextntz); + len += NSPV_rwntz(rwflag,&serialized[len],&ptr->ntz); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->reqheight),&ptr->reqheight); return(len); } @@ -275,27 +346,27 @@ void NSPV_ntzsresp_copy(struct NSPV_ntzsresp *dest,struct NSPV_ntzsresp *ptr) void NSPV_ntzsresp_purge(struct NSPV_ntzsresp *ptr) { - if ( ptr != 0 ) + if (ptr != nullptr) memset(ptr,0,sizeof(*ptr)); } -int32_t NSPV_rwinforesp(int32_t rwflag,uint8_t *serialized,struct NSPV_inforesp *ptr) +int32_t NSPV_rwinforesp(int32_t rwflag, uint8_t* serialized, struct NSPV_inforesp* ptr) { int32_t len = 0; - len += NSPV_rwntz(rwflag,&serialized[len],&ptr->notarization); - len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->blockhash),(uint8_t *)&ptr->blockhash); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->hdrheight),&ptr->hdrheight); - len += NSPV_rwequihdr(rwflag,&serialized[len],&ptr->H); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->version),&ptr->version); -//fprintf(stderr,"getinfo rwlen.%d\n",len); - return(len); + len += NSPV_rwntz(rwflag, &serialized[len], &ptr->ntz); + len += iguana_rwbignum(rwflag, &serialized[len], sizeof(ptr->blockhash), (uint8_t*)&ptr->blockhash); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->height), &ptr->height); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->hdrheight), &ptr->hdrheight); + len += NSPV_rwequihdr(rwflag, &serialized[len], &ptr->H); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->version), &ptr->version); + //fprintf(stderr,"getinfo rwlen.%d\n",len); + return (len); } -void NSPV_inforesp_purge(struct NSPV_inforesp *ptr) +void NSPV_inforesp_purge(struct NSPV_inforesp* ptr) { - if ( ptr != 0 ) - memset(ptr,0,sizeof(*ptr)); + if (ptr != nullptr) + memset(ptr, 0, sizeof(*ptr)); } int32_t NSPV_rwtxproof(int32_t rwflag,uint8_t *serialized,struct NSPV_txproof *ptr) @@ -328,73 +399,58 @@ void NSPV_txproof_copy(struct NSPV_txproof *dest,struct NSPV_txproof *ptr) void NSPV_txproof_purge(struct NSPV_txproof *ptr) { - if ( ptr != 0 ) - { - if ( ptr->tx != 0 ) + if (ptr != nullptr) { + if (ptr->tx != nullptr) free(ptr->tx); - if ( ptr->txproof != 0 ) + if (ptr->txproof != nullptr) free(ptr->txproof); - memset(ptr,0,sizeof(*ptr)); + memset(ptr, 0, sizeof(*ptr)); } } -int32_t NSPV_rwntzproofshared(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzproofshared *ptr) +int32_t NSPV_rwntzproofshared(int32_t rwflag, uint8_t* serialized, struct NSPV_ntzproofshared* ptr) { int32_t len = 0; - len += iguana_rwequihdrvec(rwflag,&serialized[len],&ptr->numhdrs,&ptr->hdrs); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->prevht),&ptr->prevht); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nextht),&ptr->nextht); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad32),&ptr->pad32); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad16),&ptr->pad16); + len += iguana_rwequihdrvec(rwflag, &serialized[len], &ptr->numhdrs, &ptr->hdrs); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->nextht), &ptr->nextht); + //len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad32),&ptr->pad32); + //len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad16),&ptr->pad16); //fprintf(stderr,"rwcommon prev.%d next.%d\n",ptr->prevht,ptr->nextht); - return(len); + return (len); } -int32_t NSPV_rwntzsproofresp(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzsproofresp *ptr) +int32_t NSPV_rwntzsproofresp(int32_t rwflag, uint8_t* serialized, struct NSPV_ntzsproofresp* ptr) { int32_t len = 0; - len += NSPV_rwntzproofshared(rwflag,&serialized[len],&ptr->common); - len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->prevtxid),(uint8_t *)&ptr->prevtxid); - len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->nexttxid),(uint8_t *)&ptr->nexttxid); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->prevtxidht),&ptr->prevtxidht); - len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nexttxidht),&ptr->nexttxidht); - len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->prevtxlen,&ptr->prevntz); - len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->nexttxlen,&ptr->nextntz); + len += NSPV_rwntzproofshared(rwflag, &serialized[len], &ptr->common); + len += iguana_rwbignum(rwflag, &serialized[len], sizeof(ptr->nexttxid), (uint8_t*)&ptr->nexttxid); + len += iguana_rwnum(rwflag, &serialized[len], sizeof(ptr->nexttxidht), &ptr->nexttxidht); + len += iguana_rwuint8vec(rwflag, &serialized[len], &ptr->nexttxlen, &ptr->nextntz); //fprintf(stderr,"retlen.%d\n",len); - return(len); + return (len); } -void NSPV_ntzsproofresp_copy(struct NSPV_ntzsproofresp *dest,struct NSPV_ntzsproofresp *ptr) +void NSPV_ntzsproofresp_copy(struct NSPV_ntzsproofresp* dest, struct NSPV_ntzsproofresp* ptr) { *dest = *ptr; - if ( ptr->common.hdrs != 0 ) - { - dest->common.hdrs = (struct NSPV_equihdr *)malloc(ptr->common.numhdrs * sizeof(*ptr->common.hdrs)); - memcpy(dest->common.hdrs,ptr->common.hdrs,ptr->common.numhdrs * sizeof(*ptr->common.hdrs)); - } - if ( ptr->prevntz != 0 ) - { - dest->prevntz = (uint8_t *)malloc(ptr->prevtxlen); - memcpy(dest->prevntz,ptr->prevntz,ptr->prevtxlen); + if (ptr->common.hdrs != 0) { + dest->common.hdrs = (struct NSPV_equihdr*)malloc(ptr->common.numhdrs * sizeof(*ptr->common.hdrs)); + memcpy(dest->common.hdrs, ptr->common.hdrs, ptr->common.numhdrs * sizeof(*ptr->common.hdrs)); } - if ( ptr->nextntz != 0 ) - { - dest->nextntz = (uint8_t *)malloc(ptr->nexttxlen); - memcpy(dest->nextntz,ptr->nextntz,ptr->nexttxlen); + if (ptr->nextntz != 0) { + dest->nextntz = (uint8_t*)malloc(ptr->nexttxlen); + memcpy(dest->nextntz, ptr->nextntz, ptr->nexttxlen); } } -void NSPV_ntzsproofresp_purge(struct NSPV_ntzsproofresp *ptr) +void NSPV_ntzsproofresp_purge(struct NSPV_ntzsproofresp* ptr) { - if ( ptr != 0 ) - { - if ( ptr->common.hdrs != 0 ) + if (ptr != nullptr) { + if (ptr->common.hdrs != nullptr) free(ptr->common.hdrs); - if ( ptr->prevntz != 0 ) - free(ptr->prevntz); - if ( ptr->nextntz != 0 ) + if (ptr->nextntz != nullptr) free(ptr->nextntz); - memset(ptr,0,sizeof(*ptr)); + memset(ptr, 0, sizeof(*ptr)); } } @@ -481,13 +537,12 @@ int32_t NSPV_txextract(CTransaction &tx,uint8_t *data,int32_t datalen) { rawdata.resize(datalen); memcpy(&rawdata[0],data,datalen); - if ( DecodeHexTx(tx,HexStr(rawdata)) != 0 ) + if ( DecodeHexTx(tx,HexStr(rawdata)) != false ) return(0); } return(-1); } -bool NSPV_SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey,uint32_t nTime); int32_t NSPV_fastnotariescount(CTransaction tx,uint8_t elected[64][33],uint32_t nTime) { @@ -562,44 +617,76 @@ int32_t NSPV_notariescount(CTransaction tx,uint8_t elected[64][33]) return(numsigs); } -uint256 NSPV_opretextract(int32_t *heightp,uint256 *blockhashp,char *symbol,std::vector opret,uint256 txid) +/*int32_t NSPV_opretextract(int32_t* heightp, uint256* blockhashp, uint256 *desttxidp, const char* symbol, vuint8_t vopret) { - uint256 desttxid; int32_t i; - iguana_rwnum(0,&opret[32],sizeof(*heightp),heightp); - for (i=0; i<32; i++) - ((uint8_t *)blockhashp)[i] = opret[i]; + //uint256 desttxid; int32_t i; + int32_t read = iguana_rwnum(0, &vopret[0], sizeof(*blockhashp), blockhashp); + read += iguana_rwnum(0, &vopret[32], sizeof(*heightp), heightp); + // detect is back + bool isBack = (ASSETCHAINS_SYMBOL[0] != 0); + if (!isBack) { + // check sybol is KMD? + vopret.size() > read + 32) + } + + + iguana_rwnum(0, &vopret[32], sizeof(*heightp), heightp); + + for (i = 0; i < 32; i++) + ((uint8_t *)blockhashp)[i] = vopret[i]; for (i=0; i<32; i++) - ((uint8_t *)&desttxid)[i] = opret[4 + 32 + i]; - if ( 0 && *heightp != 2690 ) - fprintf(stderr," ntzht.%d %s <- txid.%s size.%d\n",*heightp,(*blockhashp).GetHex().c_str(),(txid).GetHex().c_str(),(int32_t)opret.size()); + ((uint8_t *)&desttxid)[i] = vopret[4 + 32 + i]; + //if ( 0 && *heightp != 2690 ) + // fprintf(stderr," ntzht.%d %s <- txid.%s size.%d\n",*heightp,(*blockhashp).GetHex().c_str(),(txid).GetHex().c_str(),(int32_t)opret.size()); return(desttxid); -} + Notarisation nota; + if (E_UNMARSHAL(vopret, ss >> nota)) + return 0; + return -1; +}*/ -int32_t NSPV_notarizationextract(int32_t verifyntz,int32_t *ntzheightp,uint256 *blockhashp,uint256 *desttxidp,CTransaction tx) +int32_t NSPV_notarizationextract(int32_t verifyntz, int32_t* ntzheightp, uint256* blockhashp, uint256* desttxidp, int16_t *momdepthp, CTransaction tx) { - int32_t numsigs=0; uint8_t elected[64][33]; char *symbol; std::vector opret; uint32_t nTime; + vuint8_t vopret; + AssertLockHeld(cs_main); + if ( tx.vout.size() >= 2 ) { - symbol = (ASSETCHAINS_SYMBOL[0] == 0) ? (char *)"KMD" : ASSETCHAINS_SYMBOL; - GetOpReturnData(tx.vout[1].scriptPubKey,opret); - if ( opret.size() >= 32*2+4 ) + //const char *symbol = (ASSETCHAINS_SYMBOL[0] == 0) ? "KMD" : ASSETCHAINS_SYMBOL; + NotarisationData nota; + GetOpReturnData(tx.vout[1].scriptPubKey, vopret); + //if ( vopret.size() >= 32*2+4 ) + if (!vopret.empty() && E_UNMARSHAL(vopret, ss >> nota)) { //sleep(1); // needed to avoid no pnodes error - *desttxidp = NSPV_opretextract(ntzheightp,blockhashp,symbol,opret,tx.GetHash()); - nTime = NSPV_blocktime(*ntzheightp); - komodo_notaries(elected,*ntzheightp,nTime); - if ( verifyntz != 0 && (numsigs= NSPV_fastnotariescount(tx,elected,nTime)) < 12 ) + //*desttxidp = NSPV_opretextract(ntzheightp, blockhashp, symbol, vopret); + *ntzheightp = nota.height; + *blockhashp = nota.blockHash; + *desttxidp = nota.txHash; + *momdepthp = nota.MoMDepth; + if (verifyntz != 0) { - fprintf(stderr,"numsigs.%d error\n",numsigs); - return(-3); - } + uint32_t nTime = NSPV_blocktime(*ntzheightp); + int32_t numsigs=0; + uint8_t elected[64][33]; + + komodo_notaries(elected,*ntzheightp,nTime); + if ((numsigs= NSPV_fastnotariescount(tx,elected,nTime)) < 12 ) + { + LogPrintf("%s error notaries numsigs.%d less than required min 12\n", __func__, numsigs); + return(-3); + } + } return(0); } else { - fprintf(stderr,"opretsize.%d error\n",(int32_t)opret.size()); + LogPrintf("%s opretsize.%d error\n", __func__, (int32_t)vopret.size()); return(-2); } - } else return(-1); + } else { + LogPrintf("%s tx.vout.size().%d error\n", __func__, (int32_t)tx.vout.size()); + return(-1); + } } #endif // KOMODO_NSPV_H diff --git a/src/komodo_nSPV_defs.h b/src/komodo_nSPV_defs.h index 0ddaa3cb498..79569feef10 100644 --- a/src/komodo_nSPV_defs.h +++ b/src/komodo_nSPV_defs.h @@ -17,49 +17,207 @@ #ifndef KOMODO_NSPV_DEFSH #define KOMODO_NSPV_DEFSH -#define NSPV_PROTOCOL_VERSION 0x00000004 +#include +#include "uint256.h" +#include "univalue.h" +#include "script/script.h" +#include "primitives/transaction.h" +#include "amount.h" +#include "main.h" + +#define NSPV_PROTOCOL_VERSION 0x00000006 #define NSPV_POLLITERS 200 #define NSPV_POLLMICROS 50000 #define NSPV_MAXVINS 64 #define NSPV_AUTOLOGOUT 777 #define NSPV_BRANCHID 0x76b809bb +#define NSPV_MAXJSONREQUESTSIZE 65536 +#define NSPV_REQUESTIDSIZE 4 // nSPV defines and struct definitions with serialization and purge functions +// nSPV request/response message ids: + +// get info request +// params: +// int32 requestHeight - chain height to return info at #define NSPV_INFO 0x00 + +// get info response +// see: NSPV_inforesp struct #define NSPV_INFORESP 0x01 + +// get utxos for an address +// params: +// char coinaddr[KOMODO_ADDRESS_BUFSIZE] address or index key to get utxos from +// int32_t skipcount - how many records to skip +// int32_t maxrecords - max records to return (max is 32767) +// uint8_t isCC = 0; #define NSPV_UTXOS 0x02 + +// get utxos response +// see NSPV_utxosresp struct #define NSPV_UTXOSRESP 0x03 + +// get notarisation transaction id for a height +// params: +// int32_t height - chain height to search a nota for #define NSPV_NTZS 0x04 + +// get notarisation transaction id response +// see NSPV_ntzsresp struct #define NSPV_NTZSRESP 0x05 + +// get notarisation proof for a notarisation txid +// params: +// uint256 ntztxid #define NSPV_NTZSPROOF 0x06 + +// get notarisation proof response +// see NSPV_ntzsproofresp struct #define NSPV_NTZSPROOFRESP 0x07 + +// get transaction proof for a txid +// params +// uint256 txid - txid of a transaction +// int32_t height - ignored +// int32_t vout - returns the output amount for this output index #define NSPV_TXPROOF 0x08 + +// get tx proof response +// see NSPV_txproof struct #define NSPV_TXPROOFRESP 0x09 + +// get spent info for a tx output from the addressindex +// returns txproof and spending txid +// params: +// int32_t vout - tx output index +// uint256 txid - txid #define NSPV_SPENTINFO 0x0a + +// get spent info response +// see NSPV_spentinfo struct #define NSPV_SPENTINFORESP 0x0b + +// broadcast a transaction +// params: +// uint256 txid - txid of the broadcasted transaction +// int32_t txlen - length of the transaction in bytes +// uint8_t txbin[txlen] - serialised transaction #define NSPV_BROADCAST 0x0c + +// broadcast a transaction response +// see NSPV_broadcastresp #define NSPV_BROADCASTRESP 0x0d + +// get transactions inputs and outputs for an address/index key +// returns spending transactions inputs' txid/index and amount and transactions outputs txid/index and amount +// params: +// char coinaddr[KOMODO_ADDRESS_BUFSIZE] - address or index key to get inputs outputs for +// int32_t skipCount - how many records to skip +// int32_t maxRecords - how many records to return +// uint8_t isCC - is CC (1) or normal (0) address #define NSPV_TXIDS 0x0e + +// get transactions inputs and outputs response +// see NSPV_txidsresp #define NSPV_TXIDSRESP 0x0f + +// get mempool transaction inputs/outputs (deprecated) +// params: +// char coinaddr[KOMODO_ADDRESS_BUFSIZE] - address or index key to get tx for +// int32_t vout - encoded evalcode or funcid (for cc transactions) +// uint256 txid - filter results by txid (may be null) +// uint8_t funcid - filter by funcId in opreturn (for cc ) +// uint8_t isCC - is cc or normal address #define NSPV_MEMPOOL 0x10 + +// get mempool transaction inputs/outputs response +// see NSPV_mempoolresp #define NSPV_MEMPOOLRESP 0x11 + +// get cc utxos by filter (deprecated) +// params: +// char coinaddr[KOMODO_ADDRESS_BUFSIZE] - address or index key to get tx for +// CAmount amount - amount to find utxos for +// uint8_t evalcode - cc module evalcode +// uint8_t funcid[27] - funcids array to filter by funcId in opreturn (for cc ) +// uint256 filtertxid - filter results by txid (may be null) #define NSPV_CCMODULEUTXOS 0x12 + +// get cc utxos by filter response +// see NSPV_utxosresp struct #define NSPV_CCMODULEUTXOSRESP 0x13 + +// call an rpc from enabled list +// param: +// json object with rpc params (can also contain "mypk: parameter if getting tx inputs for this pubkey is requested in the rpc) +#define NSPV_REMOTERPC 0x14 + +// call an rpc response +// see NSPV_remoterpcresp struct +#define NSPV_REMOTERPCRESP 0x15 + +// reserved +#define NSPV_TRANSACTIONS 0x16 +// reserved +#define NSPV_TRANSACTIONSRESP 0x17 + +// get transactions inputs and outputs for an address/index key (yet another version) +// returns spending transactions inputs' txid/index and amount and transactions outputs txid/index and amount +// params: +// char coinaddr[KOMODO_ADDRESS_BUFSIZE] - address or index key to get inputs outputs for +// int32_t beginHeight - starting height to search txid from +// int32_t endHeight - ending height to search txid up to (inclusive) +// uint8_t isCC - is CC (1) or normal (0) address +#define NSPV_TXIDS_V2 0x18 + +// get transactions inputs and outputs response +// see NSPV_txidsresp +#define NSPV_TXIDSRESP_V2 0x19 + +// error response for an NSPV request +// params: +// int32_t errorId +// string errorDesc - network serialised error description +#define NSPV_ERRORRESP 0xff + +#define NSPV_MAX_REQ NSPV_TXIDS_V2 + + #define NSPV_MEMPOOL_ALL 0 #define NSPV_MEMPOOL_ADDRESS 1 #define NSPV_MEMPOOL_ISSPENT 2 #define NSPV_MEMPOOL_INMEMPOOL 3 #define NSPV_MEMPOOL_CCEVALCODE 4 #define NSPV_CC_TXIDS 16 -#define NSPV_REMOTERPC 0x14 -#define NSPV_REMOTERPCRESP 0x15 + + +#define NSPV_ERROR_INVALID_REQUEST_TYPE (-10) +#define NSPV_ERROR_INVALID_REQUEST_DATA (-11) +#define NSPV_ERROR_GETINFO_FIRST (-12) +#define NSPV_ERROR_INVALID_VERSION (-13) +#define NSPV_ERROR_READ_DATA (-14) +#define NSPV_ERROR_INVALID_RESPONSE (-15) +#define NSPV_ERROR_BROADCAST (-16) +#define NSPV_ERROR_REMOTE_RPC (-17) +#define NSPV_ERROR_DEPRECATED (-18) + +#define NSPV_MAXREQSPERSEC 15 + +#ifndef KOMODO_NSPV_FULLNODE +#define KOMODO_NSPV_FULLNODE (KOMODO_NSPV <= 0) +#endif // !KOMODO_NSPV_FULLNODE +#ifndef KOMODO_NSPV_SUPERLITE +#define KOMODO_NSPV_SUPERLITE (KOMODO_NSPV > 0) +#endif // !KOMODO_NSPV_SUPERLITE int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int32_t height,CTransaction &tx,uint256 &hashblock,int32_t &txheight,int32_t ¤theight,int64_t extradata,uint32_t tiptime,int64_t &rewardsum); UniValue NSPV_spend(char *srcaddr,char *destaddr,int64_t satoshis); extern uint256 SIG_TXHASH; uint32_t NSPV_blocktime(int32_t hdrheight); +// komodo block header struct NSPV_equihdr { int32_t nVersion; @@ -69,40 +227,58 @@ struct NSPV_equihdr uint32_t nTime; uint32_t nBits; uint256 nNonce; + uint64_t nSolutionLen; uint8_t nSolution[1344]; }; +// utxo data struct NSPV_utxoresp { - uint256 txid; - int64_t satoshis,extradata; - int32_t vout,height; + uint256 txid; // transaction id + int64_t satoshis, // output amount + extradata; // some data impl dependent + int32_t vout, // output index + height; // block height + uint8_t *script; // output script + uint64_t script_size; // output script size }; +// unspent transaction outputs response struct for NSPV_UTXOSRESP struct NSPV_utxosresp { - struct NSPV_utxoresp *utxos; - char coinaddr[64]; - int64_t total,interest; - int32_t nodeheight,skipcount,filter; - uint16_t numutxos,CCflag; + struct NSPV_utxoresp *utxos; // returned utxo array + char coinaddr[64]; // utxo address/index key + int64_t total, // total amount for the utxos + interest; // interest for the amount for KMD chain + int32_t nodeheight, // current height for this node + skipcount, // how many was skipped + maxrecords; // max records used to return + uint16_t numutxos, // number of the returned utxos + CCflag; // is cc (if 1) or normal (if 0) outputs were found }; +// spending input or unspent output data struct NSPV_txidresp { - uint256 txid; - int64_t satoshis; - int32_t vout,height; + uint256 txid; // transaction id + int64_t satoshis; // spent input amount (if negative) or output amount (if positive) + int32_t index; // input/output index + int32_t height; // tx block height }; +// transaction inputs/outputs response struct for NSPV_TXIDSRESP / NSPV_TXIDSRESP_V2 struct NSPV_txidsresp { - struct NSPV_txidresp *txids; + struct NSPV_txidresp *txids; // tx inputs/ouputs array char coinaddr[64]; - int32_t nodeheight,skipcount,filter; - uint16_t numtxids,CCflag; + int32_t nodeheight, + skipcount, + maxrecords; + uint16_t numtxids, + CCflag; }; +// mempool inputs/outputs info for NSPV_MEMPOOLRESP struct NSPV_mempoolresp { uint256 *txids; @@ -112,86 +288,141 @@ struct NSPV_mempoolresp uint16_t numtxids; uint8_t CCflag, funcid; }; +// notarisation tx data for NSPV_INFORESP struct NSPV_ntz { - uint256 blockhash,txid,othertxid; - int32_t height,txidheight; - uint32_t timestamp; + uint256 txid; // notarization txid + uint256 desttxid; // for back notarizations this is notarization txid from KMD/BTC chain + uint256 ntzblockhash; // notarization tx blockhash + int32_t txidheight; // notarization tx height + int32_t ntzheight; // notarized height by this notarization tx + int16_t depth; + //uint256 blockhash, txid, othertxid; + //int32_t height, txidheight; + uint32_t timestamp; // timestamp of the notarization tx block }; +// response struct for NSPV_NTZRESP struct NSPV_ntzsresp { - struct NSPV_ntz prevntz,nextntz; + struct NSPV_ntz ntz; int32_t reqheight; }; +// response struct for NSPV_INFORESP struct NSPV_inforesp { - struct NSPV_ntz notarization; - uint256 blockhash; - int32_t height,hdrheight; - struct NSPV_equihdr H; - uint32_t version; + struct NSPV_ntz ntz; // last notarisation + uint256 blockhash; // chain tip blockhash + int32_t height; // chain tip height + int32_t hdrheight; // requested block height (tip height if the requested height is 0) + struct NSPV_equihdr H; // requested block header (tip if the requested height is 0) + uint32_t version; // NSPV protocol version }; +// response struct for NSPV_TXPROOFRESP struct NSPV_txproof { - uint256 txid; - int64_t unspentvalue; - int32_t height,vout,txlen,txprooflen; - uint8_t *tx,*txproof; - uint256 hashblock; + uint256 txid; // txid of the tx + int64_t unspentvalue; // amount of the tx vout + int32_t height, // block height for the tx + vout, // + txlen, // spending tx length + txprooflen; // txproof length + uint8_t *tx, // serialised spending tx + *txproof; // serialised tx proof + uint256 hashblock; // hash of the block with the spending tx }; +// block headers notarised by a notary tx struct NSPV_ntzproofshared { - struct NSPV_equihdr *hdrs; - int32_t prevht,nextht,pad32; - uint16_t numhdrs,pad16; + struct NSPV_equihdr *hdrs; // notarised komodo headers (consecutive) + int32_t nextht; // first block height + /* int32_t pad32; */ + uint16_t numhdrs; // headers number + /* uint16_t pad16; */ }; +// response struct for NSPV_NTZSPROOFRESP struct NSPV_ntzsproofresp { - struct NSPV_ntzproofshared common; - uint256 prevtxid,nexttxid; - int32_t prevtxidht,nexttxidht,prevtxlen,nexttxlen; - uint8_t *prevntz,*nextntz; + struct NSPV_ntzproofshared common; // block headers in the notarisation MoM + uint256 nexttxid; // notarisation txid + int32_t nexttxidht, // notarised block height + nexttxlen; // notarisation tx length + uint8_t *nextntz; // notarisation tx }; +// reserved struct NSPV_MMRproof { struct NSPV_ntzproofshared common; // tbd }; +// response struct for NSPV_SPENTINFORESP struct NSPV_spentinfo { - struct NSPV_txproof spent; - uint256 txid; - int32_t vout,spentvini; + struct NSPV_txproof spent; // transaction proof of the spending tx + uint256 txid; // txid of the tx to get spending info of + int32_t vout, // tx output index to get spending info of + spentvini; // spending tx vin index }; +// response struct for NSPV_BROADCASTRESP struct NSPV_broadcastresp { uint256 txid; int32_t retcode; }; +// response struct for NSPV_CCMODULEUTXOSRESP (deprecated) struct NSPV_CCmtxinfo { struct NSPV_utxosresp U; struct NSPV_utxoresp used[NSPV_MAXVINS]; }; +// response struct for NSPV_REMOTERPCRESP struct NSPV_remoterpcresp { char method[64]; char *json; }; +/*struct NSPV_ntzargs +{ + uint256 txid; // notarization txid + uint256 desttxid; // for back notarizations this is notarization txid from KMD/BTC chain + uint256 blockhash; // notarization tx blockhash + int32_t txidht; // notarization tx height + int32_t ntzheight; // notarized height by this notarization tx +};*/ + +extern struct NSPV_inforesp NSPV_inforesult; + +void NSPV_CCunspents(std::vector>& unspentOutputs, char* coinaddr, bool ccflag); +void NSPV_CCindexOutputs(std::vector>& indexOutputs, char* coinaddr, bool ccflag); +void NSPV_CCtxids(std::vector& txids, char* coinaddr, bool ccflag, uint8_t evalcode, uint256 filtertxid, uint8_t func); +bool NSPV_SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey,uint32_t nTime); + +UniValue NSPV_getinfo_req(int32_t reqht); +UniValue NSPV_login(char *wifstr); +UniValue NSPV_logout(); +UniValue NSPV_addresstxids(char *coinaddr,int32_t CCflag,int32_t skipcount,int32_t filter); +UniValue NSPV_addressutxos(char *coinaddr,int32_t CCflag,int32_t skipcount,int32_t filter); +UniValue NSPV_mempooltxids(char *coinaddr,int32_t CCflag,uint8_t funcid,uint256 txid,int32_t vout); +UniValue NSPV_broadcast(char *hex); +UniValue NSPV_spend(char *srcaddr,char *destaddr,int64_t satoshis); +UniValue NSPV_spentinfo(uint256 txid,int32_t vout); +UniValue NSPV_notarizations(int32_t height); +UniValue NSPV_hdrsproof(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); + +int32_t bitweight(uint64_t x); -void NSPV_CCunspents(std::vector > &unspentOutputs,char *coinaddr,bool ccflag); -void NSPV_CCtxids(std::vector > &txids,char *coinaddr,bool ccflag); -void NSPV_CCtxids(std::vector &txids,char *coinaddr,bool ccflag, uint8_t evalcode,uint256 filtertxid, uint8_t func); +extern std::map nspvErrors; #endif // KOMODO_NSPV_DEFSH diff --git a/src/komodo_nSPV_fullnode.h b/src/komodo_nSPV_fullnode.h index e2e8cb49b90..800ff039a1f 100644 --- a/src/komodo_nSPV_fullnode.h +++ b/src/komodo_nSPV_fullnode.h @@ -19,74 +19,126 @@ // NSPV_get... functions need to return the exact serialized length, which is the size of the structure minus size of pointers, plus size of allocated data +#include + +#include "main.h" +#include "komodo_defs.h" #include "notarisationdb.h" #include "rpc/server.h" +#include "cc/CCinclude.h" +#include "komodo_nSPV_defs.h" +#include "komodo_nSPV.h" + +std::map nspvErrors = { + { NSPV_ERROR_INVALID_REQUEST_TYPE, "invalid request type" }, + { NSPV_ERROR_INVALID_REQUEST_DATA, "invalid request data" }, + { NSPV_ERROR_GETINFO_FIRST, "request getinfo to connect to nspv" }, + { NSPV_ERROR_INVALID_VERSION, "version not supported" }, + { NSPV_ERROR_READ_DATA, "could not get chain data" }, + { NSPV_ERROR_INVALID_RESPONSE, "could not create response message" }, + { NSPV_ERROR_BROADCAST, "could not broadcast transaction" }, + { NSPV_ERROR_REMOTE_RPC, "could not execute rpc" }, + { NSPV_ERROR_DEPRECATED, "request deprecated" }, +}; 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},{"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}, +{"channelsopen", false},{"channelspayment", false},{"channelsclose", false},{"channelsrefund", false}, +{"channelslist", false},{"channelsinfo", false},{"oraclescreate", false},{"oraclesfund", false},{"oraclesregister", false},{"oraclessubscribe", false}, +{"oraclesdata", false},{"oraclesinfo", false},{"oracleslist", false},{"gatewaysbind", false},{"gatewaysdeposit", false},{"gatewayswithdraw", false}, +{"gatewayswithdrawsign", false},{"gatewaysmarkdone", false},{"gatewayspendingsignwithdraws", false},{"gatewayssignedwithdraws", false}, +{"gatewaysinfo", false},{"gatewayslist", false},{"faucetfund", false},{"faucetget", false},{"pegscreate", false},{"pegsfund", false},{"pegsget", false},{"pegsclose", false}, +{"pegsclose", false},{"pegsredeem", false},{"pegsexchange", false},{"pegsliquidate", false},{"pegsaccounthistory", false},{"pegsaccountinfo", false},{"pegsworstaccounts", false}, +{"pegsinfo", false}, // tokens: - { "tokenask", true }, { "tokenbid", true }, { "tokenfillask", true }, { "tokenfillbid", true }, { "tokencancelask", true }, { "tokencancelbid", true }, - { "tokenorders", true }, { "mytokenorders", true }, { "tokentransfer", true },{ "tokencreate", true }, + { "tokenask", false }, { "tokenbid", false }, { "tokenfillask", false }, { "tokenfillbid", false }, { "tokencancelask", false }, { "tokencancelbid", false }, + { "tokenorders", false }, { "mytokenorders", false }, { "tokentransfer", false },{ "tokencreate", false }, { "tokenv2ask", true }, { "tokenv2bid", true }, { "tokenv2fillask", true }, { "tokenv2fillbid", true }, { "tokenv2cancelask", true }, { "tokenv2cancelbid", true }, - { "tokenv2orders", true }, { "mytokenv2orders", true }, { "tokenv2transfer", true },{ "tokenv2create", true } + { "tokenv2orders", true }, { "mytokenv2orders", true }, { "tokenv2transfer", false }, { "tokenv2create", false }, { "tokenv2address", true }, + // nspv helpers + { "createtxwithnormalinputs", true }, { "tokenv2addccinputs", true }, { "tokenv2infotokel", true }, { "gettransactionsmany", true }, + { "faucetaddccinputs", true }, }; -struct NSPV_ntzargs + +// search for notary txid starting from 'height' in the backward or forward direction +int32_t NSPV_notarization_find(struct NSPV_ntz* ntz, int32_t height, int32_t dir) { - uint256 txid,desttxid,blockhash; - int32_t txidht,ntzheight; -}; + int32_t ntzheight = 0; + uint256 hashBlock; + CTransaction tx; + Notarisation nota; + char* symbol; + std::vector vopret; + + symbol = (ASSETCHAINS_SYMBOL[0] == 0) ? (char*)"KMD" : ASSETCHAINS_SYMBOL; + memset(ntz, 0, sizeof(*ntz)); + + if (dir < 0) { + if ((ntz->txidheight = ScanNotarisationsDB(height, symbol, 1440, nota)) == 0) + return (-1); + } else { + if ((ntz->txidheight = ScanNotarisationsDB2(height, symbol, 1440, nota)) == 0) + return (-1); + } + ntz->txid = nota.first; + ntz->ntzheight = nota.second.height; + ntz->ntzblockhash = nota.second.blockHash; + ntz->desttxid = nota.second.txHash; + ntz->depth = nota.second.MoMDepth; + /* + if (!GetTransaction(args->txid, tx, hashBlock, false) || tx.vout.size() < 2) + return (-2); + GetOpReturnData(tx.vout[1].scriptPubKey, vopret); + if (vopret.size() >= 32 * 2 + 4) + args->desttxid = NSPV_opretextract(&args->ntzheight, &args->blockhash, symbol, vopret, args->txid); + */ + return ntz->ntzheight; +} + -int32_t NSPV_notarization_find(struct NSPV_ntzargs *args,int32_t height,int32_t dir) +// finds prev notarisation +// prev notary txid with notarized height < height +// and next notary txid with notarised height >= height +// if not found or chain not notarised returns zeroed 'prev' +int32_t NSPV_notarized_prev(struct NSPV_ntz* prev, int32_t height) { - int32_t ntzheight = 0; uint256 hashBlock; CTransaction tx; Notarisation nota; char *symbol; std::vector opret; - symbol = (ASSETCHAINS_SYMBOL[0] == 0) ? (char *)"KMD" : ASSETCHAINS_SYMBOL; - memset(args,0,sizeof(*args)); - if ( dir > 0 ) - height += 10; - if ( (args->txidht= ScanNotarisationsDB(height,symbol,1440,nota)) == 0 ) - return(-1); - args->txid = nota.first; - if ( !GetTransaction(args->txid,tx,hashBlock,false) || tx.vout.size() < 2 ) - return(-2); - GetOpReturnData(tx.vout[1].scriptPubKey,opret); - if ( opret.size() >= 32*2+4 ) - args->desttxid = NSPV_opretextract(&args->ntzheight,&args->blockhash,symbol,opret,args->txid); - return(args->ntzheight); + const int BACKWARD = -1; + memset(prev, 0, sizeof(*prev)); + + // search back + int32_t ntzbackwardht = NSPV_notarization_find(prev, height, BACKWARD); + LogPrint("nspv-details", "%s search backward ntz result ntzht.%d vs height.%d, txidht.%d\n", __func__, prev->ntzheight, height, prev->txidheight); + return 0; // always okay even if chain non-notarised } -int32_t NSPV_notarized_bracket(struct NSPV_ntzargs *prev,struct NSPV_ntzargs *next,int32_t height) +// finds next notarisation +// next notary txid with notarised height >= height +// if not found or chain not notarised returns zeroed 'next' +int32_t NSPV_notarized_next(struct NSPV_ntz* next, int32_t height) { - uint256 bhash; int32_t txidht,ntzht,nextht,i=0; - memset(prev,0,sizeof(*prev)); - memset(next,0,sizeof(*next)); - if ( (ntzht= NSPV_notarization_find(prev,height,-1)) < 0 || ntzht > height || ntzht == 0 ) - return(-1); - txidht = height+1; - while ( (ntzht= NSPV_notarization_find(next,txidht,1)) < height ) - { - nextht = next->txidht + 10*i; -//fprintf(stderr,"found forward ntz, but ntzht.%d vs height.%d, txidht.%d -> nextht.%d\n",next->ntzheight,height,txidht,nextht); - memset(next,0,sizeof(*next)); - txidht = nextht; - if ( ntzht <= 0 ) - break; - if ( i++ > 10 ) + const int FORWARD = 1; + memset(next, 0, sizeof(*next)); + + int32_t forwardht = height; + while(true) { // search until notarized height >= height + int32_t ntzforwardht = NSPV_notarization_find(next, forwardht, FORWARD); + LogPrint("nspv-details", "%s search forward ntz result ntzht.%d height.%d, txidht.%d\n", __func__, next->ntzheight, height, next->txidheight); + if (ntzforwardht > 0 && ntzforwardht < height) { + forwardht = next->txidheight+1; // search next next + } + else break; } - return(0); + return 0; // always okay even if chain non-notarised } +/* int32_t NSPV_ntzextract(struct NSPV_ntz *ptr,uint256 ntztxid,int32_t txidht,uint256 desttxid,int32_t ntzheight) { CBlockIndex *pindex; + + LOCK(cs_main); ptr->blockhash = *chainActive[ntzheight]->phashBlock; ptr->height = ntzheight; ptr->txidheight = txidht; @@ -96,33 +148,29 @@ int32_t NSPV_ntzextract(struct NSPV_ntz *ptr,uint256 ntztxid,int32_t txidht,uint ptr->timestamp = pindex->nTime; return(0); } +*/ -int32_t NSPV_getntzsresp(struct NSPV_ntzsresp *ptr,int32_t origreqheight) +int32_t NSPV_getntzsresp(struct NSPV_ntzsresp* ntzp, int32_t reqheight) { - struct NSPV_ntzargs prev,next; int32_t reqheight = origreqheight; - if ( reqheight < chainActive.LastTip()->GetHeight() ) - reqheight++; - if ( NSPV_notarized_bracket(&prev,&next,reqheight) == 0 ) - { - if ( prev.ntzheight != 0 ) - { - ptr->reqheight = origreqheight; - if ( NSPV_ntzextract(&ptr->prevntz,prev.txid,prev.txidht,prev.desttxid,prev.ntzheight) < 0 ) - return(-1); - } - if ( next.ntzheight != 0 ) - { - if ( NSPV_ntzextract(&ptr->nextntz,next.txid,next.txidht,next.desttxid,next.ntzheight) < 0 ) - return(-1); - } + if (NSPV_notarized_next(&ntzp->ntz, reqheight) >= 0) { // find notarization txid for which reqheight is in its scope (or it is zeroed if not found or the chain is not notarised) + LOCK(cs_main); + CBlockIndex *pindexNtz = komodo_chainactive(ntzp->ntz.txidheight); + if (pindexNtz != nullptr) + ntzp->ntz.timestamp = pindexNtz->nTime; // return notarization tx block timestamp for season + ntzp->reqheight = reqheight; + return sizeof(*ntzp); } - return(sizeof(*ptr)); + else + return -1; } + int32_t NSPV_setequihdr(struct NSPV_equihdr *hdr,int32_t height) { CBlockIndex *pindex; - if ( (pindex= komodo_chainactive(height)) != 0 ) + LOCK(cs_main); + + if ((pindex = komodo_chainactive(height)) != nullptr) { hdr->nVersion = pindex->nVersion; if ( pindex->pprev == 0 ) @@ -133,94 +181,107 @@ int32_t NSPV_setequihdr(struct NSPV_equihdr *hdr,int32_t height) hdr->nTime = pindex->nTime; hdr->nBits = pindex->nBits; hdr->nNonce = pindex->nNonce; - memcpy(hdr->nSolution,&pindex->nSolution[0],sizeof(hdr->nSolution)); - return(sizeof(*hdr)); + memcpy(hdr->nSolution, pindex->nSolution.data(), sizeof(hdr->nSolution)); + hdr->nSolutionLen = sizeof(hdr->nSolution); + return sizeof(*hdr); } return(-1); } int32_t NSPV_getinfo(struct NSPV_inforesp *ptr,int32_t reqheight) { - int32_t prevMoMheight,len = 0; CBlockIndex *pindex, *pindex2; struct NSPV_ntzsresp pair; - if ( (pindex= chainActive.LastTip()) != 0 ) - { - ptr->height = pindex->GetHeight(); - ptr->blockhash = pindex->GetBlockHash(); - memset(&pair,0,sizeof(pair)); - if ( NSPV_getntzsresp(&pair,ptr->height-1) < 0 ) - return(-1); - ptr->notarization = pair.prevntz; - if ( (pindex2= komodo_chainactive(ptr->notarization.txidheight)) != 0 ) - ptr->notarization.timestamp = pindex->nTime; + int32_t prevMoMheight, len = 0; + CBlockIndex *pindexTip, *pindexNtz; + LOCK(cs_main); + + if ((pindexTip = chainActive.LastTip()) != nullptr) { + ptr->height = pindexTip->GetHeight(); + ptr->blockhash = pindexTip->GetBlockHash(); + + if (NSPV_notarized_prev(&ptr->ntz, ptr->height) < 0) + return (-1); + if ((pindexNtz = komodo_chainactive(ptr->ntz.txidheight)) != 0) + ptr->ntz.timestamp = pindexNtz->nTime; //fprintf(stderr, "timestamp.%i\n", ptr->notarization.timestamp ); - if ( reqheight == 0 ) + if (reqheight == 0) reqheight = ptr->height; ptr->hdrheight = reqheight; ptr->version = NSPV_PROTOCOL_VERSION; - if ( NSPV_setequihdr(&ptr->H,reqheight) < 0 ) - return(-1); - return(sizeof(*ptr)); - } else return(-1); + if (NSPV_setequihdr(&ptr->H, reqheight) < 0) + return (-1); + return sizeof(*ptr); + } else + return (-1); } -int32_t NSPV_getaddressutxos(struct NSPV_utxosresp *ptr,char *coinaddr,bool isCC,int32_t skipcount,uint32_t filter) +int32_t NSPV_getaddressutxos(struct NSPV_utxosresp* ptr, char* coinaddr, bool isCC, int32_t skipcount, int32_t maxrecords) { - int64_t total = 0,interest=0; uint32_t locktime; int32_t ind=0,tipheight,maxlen,txheight,n = 0,len = 0; - std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,coinaddr,isCC); - maxlen = MAX_BLOCK_SIZE(tipheight) - 512; - maxlen /= sizeof(*ptr->utxos); - strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1); - ptr->CCflag = isCC; - ptr->filter = filter; - if ( skipcount < 0 ) - skipcount = 0; - if ( (ptr->numutxos= (int32_t)unspentOutputs.size()) >= 0 && ptr->numutxos < maxlen ) + CAmount total = 0LL, interest = 0LL; + uint32_t locktime; + int32_t ind = 0, tipheight, /*maxlen,*/ txheight, n = 0; + + std::vector> unspentOutputs; + SetCCunspents(unspentOutputs, coinaddr, isCC); + { + LOCK(cs_main); tipheight = chainActive.LastTip()->GetHeight(); - ptr->nodeheight = tipheight; - if ( skipcount >= ptr->numutxos ) - skipcount = ptr->numutxos-1; - ptr->skipcount = skipcount; - if ( ptr->numutxos-skipcount > 0 ) - { - ptr->utxos = (struct NSPV_utxoresp *)calloc(ptr->numutxos-skipcount,sizeof(*ptr->utxos)); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + } + + // use maxrecords + //maxlen = MAX_BLOCK_SIZE(tipheight) - 512; + //maxlen /= sizeof(*ptr->utxos); + if (maxrecords <= 0 || maxrecords >= std::numeric_limits::max()) + maxrecords = std::numeric_limits::max(); // prevent large requests + + strncpy(ptr->coinaddr, coinaddr, sizeof(ptr->coinaddr) - 1); + ptr->CCflag = isCC; + ptr->maxrecords = maxrecords; + if (skipcount < 0) + skipcount = 0; + ptr->skipcount = skipcount; + ptr->utxos = nullptr; + ptr->nodeheight = tipheight; + + int32_t script_len_total = 0; + + if (unspentOutputs.size() >= 0 && skipcount < unspentOutputs.size()) { + ptr->utxos = (struct NSPV_utxoresp*)calloc(unspentOutputs.size() - skipcount, sizeof(ptr->utxos[0])); + for (std::vector>::const_iterator it = unspentOutputs.begin() + skipcount; + it != unspentOutputs.end() && ind < maxrecords; it++) { + // if gettxout is != null to handle mempool { - // if gettxout is != null to handle mempool - { - if ( n >= skipcount && myIsutxo_spentinmempool(ignoretxid,ignorevin,it->first.txhash,(int32_t)it->first.index) == 0 ) - { - ptr->utxos[ind].txid = it->first.txhash; - ptr->utxos[ind].vout = (int32_t)it->first.index; - ptr->utxos[ind].satoshis = it->second.satoshis; - ptr->utxos[ind].height = it->second.blockHeight; - if ( ASSETCHAINS_SYMBOL[0] == 0 && it->second.satoshis >= 10*COIN ) - { - ptr->utxos[n].extradata = komodo_accrued_interest(&txheight,&locktime,ptr->utxos[ind].txid,ptr->utxos[ind].vout,ptr->utxos[ind].height,ptr->utxos[ind].satoshis,tipheight); - interest += ptr->utxos[ind].extradata; - } - ind++; - total += it->second.satoshis; + if (!myIsutxo_spentinmempool(ignoretxid, ignorevin, it->first.txhash, (int32_t)it->first.index)) { + ptr->utxos[ind].txid = it->first.txhash; + ptr->utxos[ind].vout = (int32_t)it->first.index; + ptr->utxos[ind].satoshis = it->second.satoshis; + ptr->utxos[ind].height = it->second.blockHeight; + if (IS_KMD_CHAIN() && it->second.satoshis >= 10 * COIN) { // calc interest on the kmd chain + ptr->utxos[n].extradata = komodo_accrued_interest(&txheight, &locktime, ptr->utxos[ind].txid, ptr->utxos[ind].vout, ptr->utxos[ind].height, ptr->utxos[ind].satoshis, tipheight); + interest += ptr->utxos[ind].extradata; } - n++; + ptr->utxos[ind].script = (uint8_t*)malloc(it->second.script.size()); + memcpy(ptr->utxos[ind].script, &it->second.script[0], it->second.script.size()); + ptr->utxos[ind].script_size = it->second.script.size(); + script_len_total += it->second.script.size() + 9; // add 9 for max varint script size + ind++; + total += it->second.satoshis; } + n++; } } - ptr->numutxos = ind; - if ( len < maxlen ) - { - len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->utxos)*ptr->numutxos - sizeof(ptr->utxos)); - //fprintf(stderr,"getaddressutxos for %s -> n.%d:%d total %.8f interest %.8f len.%d\n",coinaddr,n,ptr->numutxos,dstr(total),dstr(interest),len); - ptr->total = total; - ptr->interest = interest; - return(len); - } } - if ( ptr->utxos != 0 ) - free(ptr->utxos); - memset(ptr,0,sizeof(*ptr)); - return(0); + // always return a result: + ptr->numutxos = ind; + int32_t len = (int32_t)(sizeof(*ptr) + sizeof(ptr->utxos[0]) * ptr->numutxos - sizeof(ptr->utxos)) + script_len_total; + //fprintf(stderr,"getaddressutxos for %s -> n.%d:%d total %.8f interest %.8f len.%d\n",coinaddr,n,ptr->numutxos,dstr(total),dstr(interest),len); + ptr->total = total; + ptr->interest = interest; + return (len); + //if (ptr->utxos != nullptr) + // free(ptr->utxos); + //memset(ptr, 0, sizeof(*ptr)); + //return (0); } class BaseCCChecker { @@ -292,7 +353,6 @@ class DefaultCCChecker : public BaseCCChecker { isEof = ss.eof(); ); opretTxid = revuint256(opretTxid); - std::cerr << __func__ << " " << "opretEvalcode=" << opretEvalcode << " opretFuncid=" << (char)opretFuncid << " isCreateTx=" << isCreateTx << " opretTxid=" << opretTxid.GetHex() << std::endl; if( parseOk /*parseOk=true if eof reached*/|| !isEof /*more data means okay*/) { if (evalcode == opretEvalcode && std::find(funcids.begin(), funcids.end(), (char)opretFuncid) != funcids.end() && @@ -317,7 +377,7 @@ static std::map ccCheckerTable = }; // implements SPV server's part, gets cc module utxos, filtered by evalcode, funcid and txid on opret, for the specified amount -// if the amount param is 0 returns total available filtere utxo amount and returns no utxos +// if the amount param is 0 returns total available filtered utxos amount and returns no utxos // first char funcid in the string param is considered as the creation tx funcid so filtertxid is compared to the creation txid itself // for other funcids filtertxid is compared to the txid in opreturn int32_t NSPV_getccmoduleutxos(struct NSPV_utxosresp *ptr, char *coinaddr, int64_t amount, uint8_t evalcode, std::string funcids, uint256 filtertxid) @@ -333,6 +393,10 @@ int32_t NSPV_getccmoduleutxos(struct NSPV_utxosresp *ptr, char *coinaddr, int64_ std::vector > unspentOutputs; SetCCunspents(unspentOutputs, coinaddr, true); + { + LOCK(cs_main); + tipheight = chainActive.LastTip()->GetHeight(); + } maxlen = MAX_BLOCK_SIZE(tipheight) - 512; //maxlen /= sizeof(*ptr->utxos); // TODO why was this? we need maxlen in bytes, don't we? @@ -343,12 +407,11 @@ int32_t NSPV_getccmoduleutxos(struct NSPV_utxosresp *ptr, char *coinaddr, int64_ ptr->numutxos = 0; strncpy(ptr->coinaddr, coinaddr, sizeof(ptr->coinaddr) - 1); ptr->CCflag = 1; - tipheight = chainActive.LastTip()->GetHeight(); + ptr->nodeheight = tipheight; // will be checked in libnspv //} // select all appropriate utxos: - std::cerr << __func__ << " " << "searching addr=" << coinaddr << std::endl; for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { if (myIsutxo_spentinmempool(ignoretxid, ignorevin, it->first.txhash, (int32_t)it->first.index) == 0) @@ -364,8 +427,6 @@ int32_t NSPV_getccmoduleutxos(struct NSPV_utxosresp *ptr, char *coinaddr, int64_ // if a checker is set for evalcode use it otherwise use the default checker: if (baseChecker && baseChecker->checkCC(it->first.txhash, tx.vout, nvout, evalcode, funcids, filtertxid) || defaultCCChecker.checkCC(it->first.txhash, tx.vout, nvout, evalcode, funcids, filtertxid)) { - std::cerr << __func__ << " " << "filtered utxo with amount=" << tx.vout[nvout].nValue << std::endl; - struct CC_utxo utxo; utxo.txid = it->first.txhash; utxo.vout = (int32_t)it->first.index; @@ -376,11 +437,10 @@ int32_t NSPV_getccmoduleutxos(struct NSPV_utxosresp *ptr, char *coinaddr, int64_ } } else - std::cerr << __func__ << " " << "ERROR: cant load tx for txid, please reindex" << std::endl; + LogPrint("nspv", "ERROR: cant load tx for txid, please reindex\n"); } } - if (amount == 0) { // just return total value ptr->total = total; @@ -399,7 +459,7 @@ int32_t NSPV_getccmoduleutxos(struct NSPV_utxosresp *ptr, char *coinaddr, int64_ if (CC_vinselect(&abovei, &above, &belowi, &below, utxoSelected.data(), utxoSelected.size(), remains) < 0) { - std::cerr << "error CC_vinselect" << " remains=" << remains << " amount=" << amount << " abovei=" << abovei << " belowi=" << belowi << " ind=" << " utxoSelected.size()=" << utxoSelected.size() << ind << std::endl; + LOGSTREAMFN("nspv", CCLOG_INFO, stream << "error CC_vinselect" << " remains=" << remains << " amount=" << amount << " abovei=" << abovei << " belowi=" << belowi << " ind=" << " utxoSelected.size()=" << utxoSelected.size() << ind << std::endl); return 0; } if (abovei >= 0) // best is 'above' @@ -408,7 +468,7 @@ int32_t NSPV_getccmoduleutxos(struct NSPV_utxosresp *ptr, char *coinaddr, int64_ ind = belowi; else { - std::cerr << "error finding unspent" << " remains=" << remains << " amount=" << amount << " abovei=" << abovei << " belowi=" << belowi << " ind=" << " utxoSelected.size()=" << utxoSelected.size() << ind << std::endl; + LOGSTREAMFN("nspv", CCLOG_INFO, stream << "error finding unspent" << " remains=" << remains << " amount=" << amount << " abovei=" << abovei << " belowi=" << belowi << " ind=" << " utxoSelected.size()=" << utxoSelected.size() << ind << std::endl); return 0; } @@ -446,174 +506,188 @@ int32_t NSPV_getccmoduleutxos(struct NSPV_utxosresp *ptr, char *coinaddr, int64_ } } -int32_t NSPV_getaddresstxids(struct NSPV_txidsresp *ptr,char *coinaddr,bool isCC,int32_t skipcount,uint32_t filter) +int32_t NSPV_getaddresstxids(struct NSPV_txidsresp* ptr, char* coinaddr, bool isCC, bool isParamsToSkip, int32_t param1, int32_t param2) { - int32_t maxlen,txheight,ind=0,n = 0,len = 0; CTransaction tx; uint256 hashBlock; - std::vector > txids; - SetCCtxids(txids,coinaddr,isCC); - ptr->nodeheight = chainActive.LastTip()->GetHeight(); - maxlen = MAX_BLOCK_SIZE(ptr->nodeheight) - 512; - maxlen /= sizeof(*ptr->txids); - strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1); + int32_t txheight, ind = 0, len = 0; + //CTransaction tx; + //uint256 hashBlock; + int32_t skipcount = 0; + int32_t maxrecords = 0; + int32_t beginHeight = 0; + int32_t endHeight = 0; + + if (isParamsToSkip) { + skipcount = param1; + maxrecords = param2; + } else { + beginHeight = param1; + endHeight = param2; + } + + std::vector> txids; + SetAddressIndexOutputs(txids, coinaddr, isCC, beginHeight, endHeight); + std::cerr << __func__ << " isParamsToSkip=" << isParamsToSkip << " beginHeight=" << beginHeight << " endHeight=" << endHeight << std::endl; + + // using maxrecords instead: + //maxlen = MAX_BLOCK_SIZE(ptr->nodeheight) - 512; + //maxlen /= sizeof(*ptr->txids); + if (maxrecords <= 0 || maxrecords >= std::numeric_limits::max()) + maxrecords = std::numeric_limits::max(); // prevent large requests + + strncpy(ptr->coinaddr, coinaddr, sizeof(ptr->coinaddr) - 1); ptr->CCflag = isCC; - ptr->filter = filter; - if ( skipcount < 0 ) + ptr->maxrecords = maxrecords; + if (skipcount < 0) skipcount = 0; - if ( (ptr->numtxids= (int32_t)txids.size()) >= 0 && ptr->numtxids < maxlen ) + ptr->skipcount = skipcount; { - if ( skipcount >= ptr->numtxids ) - skipcount = ptr->numtxids-1; - ptr->skipcount = skipcount; - if ( ptr->numtxids-skipcount > 0 ) - { - ptr->txids = (struct NSPV_txidresp *)calloc(ptr->numtxids-skipcount,sizeof(*ptr->txids)); - for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) - { - if ( n >= skipcount ) - { - ptr->txids[ind].txid = it->first.txhash; - ptr->txids[ind].vout = (int32_t)it->first.index; - ptr->txids[ind].satoshis = (int64_t)it->second; - ptr->txids[ind].height = (int64_t)it->first.blockHeight; - ind++; - } - n++; - } + LOCK(cs_main); + ptr->nodeheight = chainActive.LastTip()->GetHeight(); + } + ptr->txids = nullptr; + + if (txids.size() >= 0 && skipcount < txids.size()) { + ptr->txids = (struct NSPV_txidresp*)calloc(txids.size() - skipcount, sizeof(ptr->txids[0])); + for (std::vector>::const_iterator it = txids.begin() + skipcount; + it != txids.end() && ind < maxrecords; it++) { + ptr->txids[ind].txid = it->first.txhash; + ptr->txids[ind].index = (int32_t)it->first.index; + ptr->txids[ind].satoshis = (int64_t)it->second; + ptr->txids[ind].height = (int64_t)it->first.blockHeight; + + ind++; } - ptr->numtxids = ind; - len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->txids)*ptr->numtxids - sizeof(ptr->txids)); - return(len); } - if ( ptr->txids != 0 ) - free(ptr->txids); - memset(ptr,0,sizeof(*ptr)); - return(0); + // always return a result: + ptr->numtxids = ind; + len = (int32_t)(sizeof(*ptr) + sizeof(ptr->txids[0]) * ptr->numtxids - sizeof(ptr->txids)); + return (len); } -int32_t NSPV_mempoolfuncs(bits256 *satoshisp,int32_t *vindexp,std::vector &txids,char *coinaddr,bool isCC,uint8_t funcid,uint256 txid,int32_t vout) +// get txids from addressindex or mempool by different criteria +// looks like as a set of ad-hoc functions and it should be rewritten +int32_t NSPV_mempoolfuncs(bits256* satoshisp, int32_t* vindexp, std::vector& txids, char* coinaddr, bool isCC, uint8_t funcid, uint256 txid, int32_t vout) { - int32_t num = 0,vini = 0,vouti = 0; uint8_t evalcode=0,func=0; std::vector vopret; char destaddr[64]; + int32_t num = 0, vini = 0, vouti = 0; + uint8_t evalcode = 0, func = 0; + std::vector vopret; + char destaddr[64]; *vindexp = -1; - memset(satoshisp,0,sizeof(*satoshisp)); - if ( funcid == NSPV_CC_TXIDS) - { - std::vector > tmp_txids; uint256 tmp_txid,hashBlock; - int32_t n=0,skipcount=vout>>16; uint8_t eval=(vout>>8)&0xFF, func=vout&0xFF; + memset(satoshisp, 0, sizeof(*satoshisp)); + if (funcid == NSPV_CC_TXIDS) { + std::vector> tmp_txids; + uint256 tmp_txid, hashBlock; + int32_t n = 0, skipcount = vout >> 16; + uint8_t eval = (vout >> 8) & 0xFF, func = vout & 0xFF; CTransaction tx; - SetCCtxids(tmp_txids,coinaddr,isCC); - if ( skipcount < 0 ) skipcount = 0; - if ( skipcount >= tmp_txids.size() ) - skipcount = tmp_txids.size()-1; - if ( tmp_txids.size()-skipcount > 0 ) - { - for (std::vector >::const_iterator it=tmp_txids.begin(); it!=tmp_txids.end(); it++) - { - if (txid!=zeroid || func!=0) - { - myGetTransaction(it->first.txhash,tx,hashBlock); - std::vector oprets; uint256 tokenid,txid; - std::vector vopret,vOpretExtra; uint8_t *script,e,f; + SetAddressIndexOutputs(tmp_txids, coinaddr, isCC); + if (skipcount < 0) + skipcount = 0; + if (skipcount >= tmp_txids.size()) + skipcount = tmp_txids.size() - 1; + if (tmp_txids.size() - skipcount > 0) { + for (std::vector>::const_iterator it = tmp_txids.begin(); it != tmp_txids.end(); it++) { + if (txid != zeroid || func != 0) { + myGetTransaction(it->first.txhash, tx, hashBlock); + std::vector oprets; + uint256 tokenid, txid; + std::vector vopret, vOpretExtra; + uint8_t *script, e, f; std::vector pubkeys; - if (DecodeTokenOpRetV1(tx.vout[tx.vout.size()-1].scriptPubKey,tokenid,pubkeys,oprets)!=0 && GetOpReturnCCBlob(oprets, vOpretExtra) && vOpretExtra.size()>0) - { - vopret=vOpretExtra; - } - else GetOpReturnData(tx.vout[tx.vout.size()-1].scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && script[0]==eval ) - { - switch (eval) - { - case EVAL_CHANNELS:EVAL_PEGS:EVAL_ORACLES:EVAL_GAMES:EVAL_IMPORTGATEWAY:EVAL_ROGUE: - E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> tmp_txid;); - if (e!=eval || (txid!=zeroid && txid!=tmp_txid) || (func!=0 && f!=func)) continue; - break; - case EVAL_TOKENS:EVAL_DICE:EVAL_DILITHIUM:EVAL_FAUCET:EVAL_LOTO:EVAL_PAYMENTS:EVAL_REWARDS: - E_UNMARSHAL(vopret,ss >> e; ss >> f;); - if (e!=eval || (func!=0 && f!=func)) continue; - break; - default: - break; + if (DecodeTokenOpRetV1(tx.vout[tx.vout.size() - 1].scriptPubKey, tokenid, pubkeys, oprets) != 0 && GetOpReturnCCBlob(oprets, vOpretExtra) && vOpretExtra.size() > 0) { + vopret = vOpretExtra; + } else + GetOpReturnData(tx.vout[tx.vout.size() - 1].scriptPubKey, vopret); + script = (uint8_t*)vopret.data(); + if (vopret.size() > 2 && script[0] == eval) { + switch (eval) { + case EVAL_CHANNELS: + EVAL_PEGS: + EVAL_ORACLES: + EVAL_GAMES: + EVAL_IMPORTGATEWAY: + EVAL_ROGUE: + E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> tmp_txid;); + if (e != eval || (txid != zeroid && txid != tmp_txid) || (func != 0 && f != func)) + continue; + break; + case EVAL_TOKENS: + EVAL_DICE: + EVAL_DILITHIUM: + EVAL_FAUCET: + EVAL_LOTO: + EVAL_PAYMENTS: + EVAL_REWARDS: + E_UNMARSHAL(vopret, ss >> e; ss >> f;); + if (e != eval || (func != 0 && f != func)) + continue; + break; + default: + break; } - } + } } - if ( n >= skipcount ) txids.push_back(it->first.txhash); + if (n >= skipcount) + txids.push_back(it->first.txhash); n++; } - return (n-skipcount); + return (n - skipcount); } return (0); } - if ( mempool.size() == 0 ) - return(0); - if ( funcid == NSPV_MEMPOOL_CCEVALCODE ) - { + if (mempool.size() == 0) + return (0); + if (funcid == NSPV_MEMPOOL_CCEVALCODE) { isCC = true; evalcode = vout & 0xff; func = (vout >> 8) & 0xff; } LOCK(mempool.cs); - BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) - { - const CTransaction &tx = e.GetTx(); - const uint256 &hash = tx.GetHash(); - if ( funcid == NSPV_MEMPOOL_ALL ) - { + BOOST_FOREACH (const CTxMemPoolEntry& e, mempool.mapTx) { + const CTransaction& tx = e.GetTx(); + const uint256& hash = tx.GetHash(); + if (funcid == NSPV_MEMPOOL_ALL) { txids.push_back(hash); num++; continue; - } - else if ( funcid == NSPV_MEMPOOL_INMEMPOOL ) - { - if ( hash == txid ) - { + } else if (funcid == NSPV_MEMPOOL_INMEMPOOL) { + if (hash == txid) { txids.push_back(hash); - return(++num); + return (++num); } continue; - } - else if ( funcid == NSPV_MEMPOOL_CCEVALCODE ) - { - if ( tx.vout.size() > 1 ) - { - CScript scriptPubKey = tx.vout[tx.vout.size()-1].scriptPubKey; - if ( GetOpReturnData(scriptPubKey,vopret) != 0 ) - { - if ( vopret[0] != evalcode || (func!=0 && vopret[1] != func) ) continue; + } else if (funcid == NSPV_MEMPOOL_CCEVALCODE) { + if (tx.vout.size() > 1) { + CScript scriptPubKey = tx.vout[tx.vout.size() - 1].scriptPubKey; + if (GetOpReturnData(scriptPubKey, vopret) != 0) { + if (vopret[0] != evalcode || (func != 0 && vopret[1] != func)) + continue; txids.push_back(hash); num++; } } continue; } - if ( funcid == NSPV_MEMPOOL_ISSPENT ) - { - BOOST_FOREACH(const CTxIn &txin,tx.vin) - { + if (funcid == NSPV_MEMPOOL_ISSPENT) { + 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) { txids.push_back(hash); *vindexp = vini; - return(++num); + return (++num); } vini++; } - } - else if ( funcid == NSPV_MEMPOOL_ADDRESS ) - { - BOOST_FOREACH(const CTxOut &txout,tx.vout) - { - if ( txout.scriptPubKey.IsPayToCryptoCondition() == isCC ) - { - Getscriptaddress(destaddr,txout.scriptPubKey); - if ( strcmp(destaddr,coinaddr) == 0 ) - { + } else if (funcid == NSPV_MEMPOOL_ADDRESS) { + BOOST_FOREACH (const CTxOut& txout, tx.vout) { + if (txout.scriptPubKey.IsPayToCryptoCondition() == isCC) { + Getscriptaddress(destaddr, txout.scriptPubKey); + if (strcmp(destaddr, coinaddr) == 0) { txids.push_back(hash); *vindexp = vouti; - if ( num < 4 ) + if (num < 4) satoshisp->ulongs[num] = txout.nValue; num++; } @@ -623,44 +697,45 @@ int32_t NSPV_mempoolfuncs(bits256 *satoshisp,int32_t *vindexp,std::vector txids; bits256 satoshis; uint256 tmp,tmpdest; int32_t i,len = 0; - ptr->nodeheight = chainActive.LastTip()->GetHeight(); - strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1); + std::vector txids; + bits256 satoshis; + uint256 tmp, tmpdest; + int32_t i, len = 0; + { + LOCK(cs_main); + ptr->nodeheight = chainActive.LastTip()->GetHeight(); + } + strncpy(ptr->coinaddr, coinaddr, sizeof(ptr->coinaddr) - 1); ptr->CCflag = isCC; ptr->txid = txid; ptr->vout = vout; ptr->funcid = funcid; - if ( NSPV_mempoolfuncs(&satoshis,&ptr->vindex,txids,coinaddr,isCC,funcid,txid,vout) >= 0 ) - { - if ( (ptr->numtxids= (int32_t)txids.size()) >= 0 ) - { - if ( ptr->numtxids > 0 ) - { - ptr->txids = (uint256 *)calloc(ptr->numtxids,sizeof(*ptr->txids)); - for (i=0; inumtxids; i++) - { + if (NSPV_mempoolfuncs(&satoshis, &ptr->vindex, txids, coinaddr, isCC, funcid, txid, vout) >= 0) { + if ((ptr->numtxids = (int32_t)txids.size()) >= 0) { + if (ptr->numtxids > 0) { + ptr->txids = (uint256*)calloc(ptr->numtxids, sizeof(*ptr->txids)); + for (i = 0; i < ptr->numtxids; i++) { tmp = txids[i]; - iguana_rwbignum(IGUANA_READ,(uint8_t *)&tmp,sizeof(*ptr->txids),(uint8_t *)&ptr->txids[i]); + iguana_rwbignum(IGUANA_READ, (uint8_t*)&tmp, sizeof(*ptr->txids), (uint8_t*)&ptr->txids[i]); } } - if ( funcid == NSPV_MEMPOOL_ADDRESS ) - { - memcpy(&tmp,&satoshis,sizeof(tmp)); - iguana_rwbignum(IGUANA_READ,(uint8_t *)&tmp,sizeof(ptr->txid),(uint8_t *)&ptr->txid); + if (funcid == NSPV_MEMPOOL_ADDRESS) { + memcpy(&tmp, &satoshis, sizeof(tmp)); + iguana_rwbignum(IGUANA_READ, (uint8_t*)&tmp, sizeof(ptr->txid), (uint8_t*)&ptr->txid); } - len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->txids)*ptr->numtxids - sizeof(ptr->txids)); - return(len); + len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->txids) * ptr->numtxids - sizeof(ptr->txids)); + return (len); } } - if ( ptr->txids != 0 ) + if (ptr->txids != 0) free(ptr->txids); - memset(ptr,0,sizeof(*ptr)); - return(0); + memset(ptr, 0, sizeof(*ptr)); + return (0); } int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n) @@ -672,7 +747,8 @@ int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n) { request.read(json,n); jreq.parse(request); - strcpy(ptr->method,jreq.strMethod.c_str()); + strncpy(ptr->method, jreq.strMethod.c_str(), sizeof(ptr->method)-1); + ptr->method[sizeof(ptr->method)-1] = '\0'; len+=sizeof(ptr->method); std::map::iterator it = nspv_remote_commands.find(jreq.strMethod); if (it==nspv_remote_commands.end()) @@ -694,6 +770,8 @@ int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n) rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); response=rpc_result.write(); ptr->json = (char*)malloc(response.size()+1); + if (ptr->json == nullptr) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Cannot allocate memory for response"); strcpy(ptr->json, response.c_str()); len+=response.size(); return (len); @@ -705,7 +783,7 @@ int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n) rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); response=rpc_result.write(); } - catch (const runtime_error& e) + catch (const std::runtime_error& e) { rpc_result = JSONRPCReplyObj(NullUniValue,JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); response=rpc_result.write(); @@ -731,7 +809,7 @@ uint8_t *NSPV_getrawtx(CTransaction &tx,uint256 &hashBlock,int32_t *txlenp,uint2 //fprintf(stderr,"error getting transaction %s\n",txid.GetHex().c_str()); return(0); } - string strHex = EncodeHexTx(tx); + std::string strHex = EncodeHexTx(tx); *txlenp = (int32_t)strHex.size() >> 1; if ( *txlenp > 0 ) { @@ -742,71 +820,86 @@ uint8_t *NSPV_getrawtx(CTransaction &tx,uint256 &hashBlock,int32_t *txlenp,uint2 return(rawtx); } -int32_t NSPV_sendrawtransaction(struct NSPV_broadcastresp *ptr,uint8_t *data,int32_t n) +int32_t NSPV_sendrawtransaction(struct NSPV_broadcastresp *ptr, uint8_t *data, int32_t n, CValidationState *pstate) { CTransaction tx; ptr->retcode = 0; if ( NSPV_txextract(tx,data,n) == 0 ) { - //LOCK(cs_main); + LOCK(cs_main); // required by AcceptToMemoryPool + + bool fMissingInputs = false; ptr->txid = tx.GetHash(); //fprintf(stderr,"try to addmempool transaction %s\n",ptr->txid.GetHex().c_str()); - if ( myAddtomempool(tx) != 0 ) + if (myAddtomempool(tx, pstate, &fMissingInputs) != false) { ptr->retcode = 1; //int32_t i; //for (i=0; itxid.GetHex().c_str(),ptr->retcode); + //fprintf(stderr," relay transaction %s retcode.%d\n",ptr->txid.GetHex().c_str(),ptr->retcode); RelayTransaction(tx); - } else ptr->retcode = -3; + } + else { + if (fMissingInputs) + ptr->retcode = -4; + else + ptr->retcode = -3; + } - } else ptr->retcode = -1; + } + else + ptr->retcode = -1; return(sizeof(*ptr)); } // get txproof for txid, // to get the proof object the height should be passed // otherwise only block hash alone will be returned -int32_t NSPV_gettxproof(struct NSPV_txproof* ptr, int32_t vout, uint256 txid, int32_t height) +// Note: not clear why we should have passed the height? Txid is sufficient, let's ignore height +int32_t NSPV_gettxproof(struct NSPV_txproof* ptr, int32_t vout, uint256 txid /*, int32_t height*/) { - int32_t flag = 0, len = 0; + int32_t len = 0; CTransaction _tx; uint256 hashBlock; CBlock block; CBlockIndex* pindex; ptr->height = -1; - if ((ptr->tx = NSPV_getrawtx(_tx, hashBlock, &ptr->txlen, txid)) != 0) { + if ((ptr->tx = NSPV_getrawtx(_tx, hashBlock, &ptr->txlen, txid)) != nullptr) { ptr->txid = txid; ptr->vout = vout; ptr->hashblock = hashBlock; - if (height == 0) + { + LOCK(cs_main); ptr->height = komodo_blockheight(hashBlock); - else { - ptr->height = height; - if ((pindex = komodo_chainactive(height)) != 0 && komodo_blockload(block, pindex) == 0) { - BOOST_FOREACH (const CTransaction& tx, block.vtx) { - if (tx.GetHash() == txid) { - flag = 1; - break; - } + pindex = komodo_chainactive(ptr->height); + } + if (pindex != nullptr && komodo_blockload(block, pindex) == 0) { + bool found = false; + BOOST_FOREACH (const CTransaction& tx, block.vtx) { + if (tx.GetHash() == txid) { + found = true; + break; } - if (flag != 0) { - set setTxids; - CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); - setTxids.insert(txid); - CMerkleBlock mb(block, setTxids); - ssMB << mb; - std::vector proof(ssMB.begin(), ssMB.end()); - ptr->txprooflen = (int32_t)proof.size(); - LogPrint("nspv-details", "%s txid.%s found txproof.(%s) height.%d\n", __func__, txid.GetHex().c_str(), HexStr(proof).c_str(), ptr->height); - if (ptr->txprooflen > 0) { - ptr->txproof = (uint8_t*)calloc(1, ptr->txprooflen); - memcpy(ptr->txproof, &proof[0], ptr->txprooflen); - } - //fprintf(stderr,"gettxproof slen.%d\n",(int32_t)(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen)); + } + if (found) { + std::set setTxids; + CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); + setTxids.insert(txid); + CMerkleBlock mb(block, setTxids); + ssMB << mb; + std::vector proof(ssMB.begin(), ssMB.end()); + ptr->txprooflen = (int32_t)proof.size(); + LogPrint("nspv-details", "%s txid.%s found txproof.(%s) height.%d\n", __func__, txid.GetHex().c_str(), HexStr(proof).c_str(), ptr->height); + if (ptr->txprooflen > 0) { + ptr->txproof = (uint8_t*)calloc(1, ptr->txprooflen); + memcpy(ptr->txproof, &proof[0], ptr->txprooflen); } + //fprintf(stderr,"gettxproof respLen.%d\n",(int32_t)(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen)); + } + else { + LogPrint("nspv-details", "%s txid=%s not found in the block of ht=%d", __func__, txid.GetHex().c_str(), ptr->height); } } ptr->unspentvalue = CCgettxout(txid, vout, 1, 1); @@ -814,257 +907,343 @@ int32_t NSPV_gettxproof(struct NSPV_txproof* ptr, int32_t vout, uint256 txid, in return (sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen); } -int32_t NSPV_getntzsproofresp(struct NSPV_ntzsproofresp *ptr,uint256 prevntztxid,uint256 nextntztxid) +// get notarization tx and headers for the notarized depth +int32_t NSPV_getntzsproofresp(struct NSPV_ntzsproofresp* ntzproofp, uint256 nextntztxid) { - int32_t i; uint256 hashBlock,bhash0,bhash1,desttxid0,desttxid1; CTransaction tx; + //uint256 prevHashBlock, bhash0, desttxid0; + uint256 nextHashBlock, bhash1, desttxid1; + + CTransaction tx; + int16_t dummy, momdepth; + const int32_t DONTVALIDATESIG = 0; + + LOCK(cs_main); + + /* ptr->prevtxid = prevntztxid; - ptr->prevntz = NSPV_getrawtx(tx,hashBlock,&ptr->prevtxlen,ptr->prevtxid); - ptr->prevtxidht = komodo_blockheight(hashBlock); - if ( NSPV_notarizationextract(0,&ptr->common.prevht,&bhash0,&desttxid0,tx) < 0 ) - return(-2); - else if ( komodo_blockheight(bhash0) != ptr->common.prevht ) - return(-3); - - ptr->nexttxid = nextntztxid; - ptr->nextntz = NSPV_getrawtx(tx,hashBlock,&ptr->nexttxlen,ptr->nexttxid); - ptr->nexttxidht = komodo_blockheight(hashBlock); - if ( NSPV_notarizationextract(0,&ptr->common.nextht,&bhash1,&desttxid1,tx) < 0 ) - return(-5); - else if ( komodo_blockheight(bhash1) != ptr->common.nextht ) - return(-6); - - else if ( ptr->common.prevht > ptr->common.nextht || (ptr->common.nextht - ptr->common.prevht) > 1440 ) - { - fprintf(stderr,"illegal prevht.%d nextht.%d\n",ptr->common.prevht,ptr->common.nextht); - return(-7); + ptr->prevntz = NSPV_getrawtx(tx, prevHashBlock, &ptr->prevtxlen, ptr->prevtxid); + ptr->prevtxidht = komodo_blockheight(prevHashBlock); + if (NSPV_notarizationextract(DONTVALIDATESIG, &ptr->common.prevht, &bhash0, &desttxid0, &dummy, tx) < 0) { + LogPrintf("%s error: cant decode notarization opreturn ptr->common.prevht.%d bhash0 %s\n", __func__, ptr->common.prevht, bhash0.ToString()); + return (-2); + } else if (komodo_blockheight(bhash0) != ptr->common.prevht) { + LogPrintf("%s error: bhash0 ht.%d not equal to prevht.%d\n", __func__, komodo_blockheight(bhash0), ptr->common.prevht); + return (-3); } - //fprintf(stderr,"%s -> prevht.%d, %s -> nexht.%d\n",ptr->prevtxid.GetHex().c_str(),ptr->common.prevht,ptr->nexttxid.GetHex().c_str(),ptr->common.nextht); - ptr->common.numhdrs = (ptr->common.nextht - ptr->common.prevht + 1); - ptr->common.hdrs = (struct NSPV_equihdr *)calloc(ptr->common.numhdrs,sizeof(*ptr->common.hdrs)); - //fprintf(stderr,"prev.%d next.%d allocate numhdrs.%d\n",prevht,nextht,ptr->common.numhdrs); - for (i=0; icommon.numhdrs; i++) - { + */ + + ntzproofp->nexttxid = nextntztxid; + ntzproofp->nextntz = NSPV_getrawtx(tx, nextHashBlock, &ntzproofp->nexttxlen, ntzproofp->nexttxid); + ntzproofp->nexttxidht = komodo_blockheight(nextHashBlock); + if (NSPV_notarizationextract(DONTVALIDATESIG, &ntzproofp->common.nextht, &bhash1, &desttxid1, &momdepth, tx) < 0) { + LogPrintf("%s error: cant decode notarization opreturn nexttxid %s ptr->common.nextht.%d bhash1 %s\n", __func__, ntzproofp->nexttxid.GetHex(), ntzproofp->common.nextht, bhash1.ToString()); + return (-5); + } else if (komodo_blockheight(bhash1) != ntzproofp->common.nextht) { + LogPrintf("%s error: bhash1 ht.%d not equal to nextht.%d\n", __func__, komodo_blockheight(bhash1), ntzproofp->common.nextht); + return (-6); + } + /*else if (ptr->common.prevht > ptr->common.nextht || (ptr->common.nextht - ptr->common.prevht) > 1440) { + LogPrintf("%s error illegal prevht.%d nextht.%d\n", __func__, ptr->common.prevht, ptr->common.nextht); + return (-7); + }*/ + //fprintf(stderr, "%s -> prevht.%d, %s -> nexht.%d\n", ptr->prevtxid.GetHex().c_str(), ptr->common.prevht, ptr->nexttxid.GetHex().c_str(), ptr->common.nextht); + ntzproofp->common.numhdrs = momdepth; + ntzproofp->common.hdrs = (struct NSPV_equihdr*)calloc(ntzproofp->common.numhdrs, sizeof(*ntzproofp->common.hdrs)); + //fprintf(stderr, "prev.%d next.%d allocate numhdrs.%d\n", ptr->common.prevht, ptr->common.nextht, ptr->common.numhdrs); + for (int32_t i = 0; i < ntzproofp->common.numhdrs; i++) { //hashBlock = NSPV_hdrhash(&ptr->common.hdrs[i]); //fprintf(stderr,"hdr[%d] %s\n",prevht+i,hashBlock.GetHex().c_str()); - if ( NSPV_setequihdr(&ptr->common.hdrs[i],ptr->common.prevht+i) < 0 ) - { - fprintf(stderr,"error setting hdr.%d\n",ptr->common.prevht+i); - free(ptr->common.hdrs); - ptr->common.hdrs = 0; - return(-1); + int32_t ht = ntzproofp->common.nextht - momdepth + 1 + i; + if (NSPV_setequihdr(&ntzproofp->common.hdrs[i], ht) < 0) { + LogPrintf("%s error setting hdr for ht.%d\n", __func__, ht); + free(ntzproofp->common.hdrs); + ntzproofp->common.hdrs = 0; + return (-1); } } - //fprintf(stderr,"sizeof ptr %ld, common.%ld lens.%d %d\n",sizeof(*ptr),sizeof(ptr->common),ptr->prevtxlen,ptr->nexttxlen); - return(sizeof(*ptr) + sizeof(*ptr->common.hdrs)*ptr->common.numhdrs - sizeof(ptr->common.hdrs) - sizeof(ptr->prevntz) - sizeof(ptr->nextntz) + ptr->prevtxlen + ptr->nexttxlen); + //fprintf(stderr, "sizeof ptr %ld, common.%ld lens.%d %d\n", sizeof(*ptr), sizeof(ptr->common), ptr->prevtxlen, ptr->nexttxlen); + return sizeof(*ntzproofp) + sizeof(*ntzproofp->common.hdrs) * (ntzproofp->common.numhdrs) + ntzproofp->nexttxlen; } -int32_t NSPV_getspentinfo(struct NSPV_spentinfo *ptr,uint256 txid,int32_t vout) +int32_t NSPV_getspentinfo(struct NSPV_spentinfo* ptr, uint256 txid, int32_t vout) { int32_t len = 0; ptr->txid = txid; ptr->vout = vout; ptr->spentvini = -1; len = (int32_t)(sizeof(*ptr) - sizeof(ptr->spent.tx) - sizeof(ptr->spent.txproof)); - if ( CCgetspenttxid(ptr->spent.txid,ptr->spentvini,ptr->spent.height,txid,vout) == 0 ) - { - if ( NSPV_gettxproof(&ptr->spent,0,ptr->spent.txid,ptr->spent.height) > 0 ) + if (CCgetspenttxid(ptr->spent.txid, ptr->spentvini, ptr->spent.height, txid, vout) == 0) { + if (NSPV_gettxproof(&ptr->spent, 0, ptr->spent.txid /*,ptr->spent.height*/) > 0) len += ptr->spent.txlen + ptr->spent.txprooflen; - else - { + else { NSPV_txproof_purge(&ptr->spent); - return(-1); + return (-1); } } - return(len); + return (len); +} + +static void NSPV_senderror(CNode* pfrom, uint32_t requestId, int32_t err) +{ + const uint8_t respCode = NSPV_ERRORRESP; + std::string errDesc = nspvErrors[err]; + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << respCode << requestId << err << errDesc; + + std::vector response = vuint8_t(ss.begin(), ss.end()); + pfrom->PushMessage("nSPV", response); +} + +static void NSPV_senderror(CNode* pfrom, uint32_t requestId, int32_t err, const std::string &errDesc) +{ + const uint8_t respCode = NSPV_ERRORRESP; + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << respCode << requestId << err << errDesc; + + std::vector response = vuint8_t(ss.begin(), ss.end()); + pfrom->PushMessage("nSPV", response); } -void komodo_nSPVreq(CNode *pfrom,std::vector request) // received a request + +// processing nspv requests +void komodo_nSPVreq(CNode* pfrom, std::vector request) // received a request { - int32_t slen, reqheight; - std::vector response; + std::vector response; uint32_t timestamp = (uint32_t)time(NULL); + uint8_t requestType = request[0]; + uint32_t requestId; + const int nspvHeaderSize = sizeof(requestType) + sizeof(requestId); - if (request.size() == 0) { - LogPrint("nspv", "empty request from peer %d\n", pfrom->id); + if (request.size() < nspvHeaderSize) { + LogPrint("nspv", "request too small from peer %d\n", pfrom->id); return; } - - int32_t len = request.size(); - - // rate limit no more 1 request/sec of same type from same node: - int32_t ind = request[0] >> 1; - if (ind >= sizeof(pfrom->prevtimes)/sizeof(*pfrom->prevtimes) ) - ind = (int32_t)(sizeof(pfrom->prevtimes)/sizeof(*pfrom->prevtimes)) - 1; - if (pfrom->prevtimes[ind] > timestamp) - pfrom->prevtimes[ind] = 0; - else if (timestamp == pfrom->prevtimes[ind]) { - LogPrint("nspv", "rate limit from peer %d\n", pfrom->id); - return; + + memcpy(&requestId, &request[1], sizeof(requestId)); + uint8_t *requestData = &request[nspvHeaderSize]; + int32_t requestDataLen = request.size() - nspvHeaderSize; + + // rate limit no more NSPV_MAXREQSPERSEC request/sec of same type from same node: + int32_t idata = requestType >> 1; + if (idata >= sizeof(pfrom->nspvdata) / sizeof(pfrom->nspvdata[0])) + idata = (int32_t)(sizeof(pfrom->nspvdata) / sizeof(pfrom->nspvdata[0])) - 1; + if (pfrom->nspvdata[idata].prevtime > timestamp) { + pfrom->nspvdata[idata].prevtime = 0; + pfrom->nspvdata[idata].nreqs = 0; + } else if (timestamp == pfrom->nspvdata[idata].prevtime) { + if (pfrom->nspvdata[idata].nreqs > NSPV_MAXREQSPERSEC) { + LogPrint("nspv", "rate limit reached from peer %d\n", pfrom->id); + return; + } + } else { + pfrom->nspvdata[idata].nreqs = 0; // clear request stat if new second } - switch(request[0]) - { - case NSPV_INFO: // info + // check if nspv connected: + if (!pfrom->fNspvConnected) { + if (requestType != NSPV_INFO) { + LogPrint("nspv", "nspv node should call NSPV_INFO first from node=%d\n", pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_GETINFO_FIRST); + return; + } + } + + switch (requestType) { + case NSPV_INFO: // info, mandatory first request { struct NSPV_inforesp I; - //fprintf(stderr,"check info %u vs %u, ind.%d\n",timestamp,pfrom->prevtimes[ind],ind); - if (len == 1 + sizeof(reqheight)) - iguana_rwnum(IGUANA_READ, &request[1], sizeof(reqheight), &reqheight); - else - reqheight = 0; + int32_t respLen; + int32_t reqheight; + uint32_t version; + + //fprintf(stderr,"check info %u vs %u, ind.%d\n", timestamp, pfrom->nspvdata[ind].prevtime, ind); + if (requestDataLen != sizeof(version) + sizeof(reqheight)) { + LogPrint("nspv", "NSPV_INFO invalid request size %d from node=%d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); + return; + } + int32_t offset = 0; + offset += iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(version), &version); + if (version != NSPV_PROTOCOL_VERSION) { + LogPrint("nspv", "NSPV_INFO nspv version %d not supported from node=%d\n", version, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_VERSION); + return; + } + + iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(reqheight), &reqheight); + //fprintf(stderr,"request height.%d\n",reqheight); memset(&I, 0, sizeof(I)); - if ((slen = NSPV_getinfo(&I, reqheight)) > 0) { - response.resize(1 + slen); + if ((respLen = NSPV_getinfo(&I, reqheight)) > 0) { + response.resize(nspvHeaderSize + respLen); response[0] = NSPV_INFORESP; - //fprintf(stderr,"slen.%d version.%d\n",slen,I.version); - if (NSPV_rwinforesp(IGUANA_WRITE, &response[1], &I) == slen) - { + memcpy(&response[1], &requestId, sizeof(requestId)); + //fprintf(stderr,"respLen.%d version.%d\n",respLen,I.version); + if (NSPV_rwinforesp(IGUANA_WRITE, &response[nspvHeaderSize], &I) <= respLen) { //fprintf(stderr,"send info resp to id %d\n",(int32_t)pfrom->id); - pfrom->PushMessage("nSPV",response); - pfrom->prevtimes[ind] = timestamp; - LogPrint("nspv-details", "NSPV_INFO response: version %d to node=%d\n", I.version, pfrom->id); + pfrom->PushMessage("nSPV", response); + pfrom->nspvdata[idata].prevtime = timestamp; + pfrom->nspvdata[idata].nreqs++; + LogPrint("nspv-details", "NSPV_INFO sent response: version %d to node=%d\n", I.version, pfrom->id); + pfrom->fNspvConnected = true; // allow to do nspv requests + } else { + LogPrint("nspv", "NSPV_rwinforesp incorrect response len.%d\n", respLen); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_RESPONSE); } - else - LogPrint("nspv", "NSPV_rwinforesp incorrect response len.%d\n", slen); NSPV_inforesp_purge(&I); - } else - LogPrint("nspv", "NSPV_getinfo error.%d\n", slen); - } + } else { + LogPrint("nspv", "NSPV_getinfo error.%d\n", respLen); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_READ_DATA); + } + } break; - case NSPV_UTXOS: + case NSPV_UTXOS: { struct NSPV_utxosresp U; - char coinaddr[KOMODO_ADDRESS_BUFSIZE]; - int32_t skipcount = 0; - uint32_t filter = 0; + char coinaddr[KOMODO_ADDRESS_BUFSIZE]; + int32_t skipcount = 0; + int32_t maxrecords = 0; uint8_t isCC = 0; - //fprintf(stderr,"utxos: %u > %u, ind.%d, len.%d\n",timestamp,pfrom->prevtimes[ind],ind,len); - - if (len < 2) { - LogPrint("nspv", "NSPV_UTXOS bad request too short len.%d node %d\n", len, pfrom->id); + int32_t respEstimated; + + //fprintf(stderr,"utxos: %u > %u, ind.%d, len.%d\n",timestamp,pfrom->nspvdata[ind].prevtime,ind,len); + + if (requestDataLen < 1) { + LogPrint("nspv", "NSPV_UTXOS bad request too short len.%d node %d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); return; } - - int32_t offset = sizeof(request[0]) + sizeof(request[1]); - int32_t addrlen = request[1]; - if (offset + addrlen > len || addrlen > sizeof(coinaddr)-1) // out of bounds + + int32_t addrlen = requestData[0]; + int32_t offset = 1; + if (offset + addrlen > requestDataLen || addrlen > sizeof(coinaddr) - 1) // out of bounds { - LogPrint("nspv", "NSPV_UTXOS bad request len.%d too short or addrlen.%d out of bounds, node=%d\n", len, addrlen, pfrom->id); + LogPrint("nspv", "NSPV_UTXOS bad request len.%d too short or addrlen.%d out of bounds, node=%d\n", requestDataLen, addrlen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); return; } - memcpy(coinaddr, &request[offset], addrlen); + memcpy(coinaddr, &requestData[offset], addrlen); coinaddr[addrlen] = 0; offset += addrlen; - if (offset + sizeof(isCC) <= len) // TODO: a bit different from others format - allows omitted params, maybe better have fixed + if (offset + sizeof(isCC) <= requestDataLen) // TODO: a bit different from others format - allows omitted params, maybe better have fixed { - isCC = (request[offset] != 0); + isCC = (requestData[offset] != 0); offset += sizeof(isCC); - if (offset + sizeof(skipcount) <= len) - { - iguana_rwnum(IGUANA_READ, &request[offset], sizeof(skipcount), &skipcount); + if (offset + sizeof(skipcount) <= requestDataLen) { + iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(skipcount), &skipcount); offset += sizeof(skipcount); - if (offset + sizeof(filter) <= len) - { - iguana_rwnum(IGUANA_READ, &request[offset], sizeof(filter), &filter); - offset += sizeof(filter); + if (offset + sizeof(maxrecords) <= requestDataLen) { + iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(maxrecords), &maxrecords); + offset += sizeof(maxrecords); } } } - if (offset != len) { - LogPrint("nspv", "NSPV_UTXOS bad request parameters format: len.%d, offset.%d, addrlen.%d, node=%d\n", len, offset, addrlen, pfrom->id); + if (offset != requestDataLen) { + LogPrint("nspv", "NSPV_UTXOS bad request parameters format: len.%d, offset.%d, addrlen.%d, node=%d\n", requestDataLen, offset, addrlen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); return; } - LogPrint("nspv-details", "NSPV_UTXOS address=%s isCC.%d skipcount.%d filter.%x\n", coinaddr, isCC, skipcount, filter); + LogPrint("nspv-details", "NSPV_UTXOS address=%s isCC.%d skipcount.%d maxrecords.%d\n", coinaddr, isCC, skipcount, maxrecords); memset(&U, 0, sizeof(U)); - if ((slen = NSPV_getaddressutxos(&U, coinaddr, isCC, skipcount, filter)) > 0) - { - response.resize(1 + slen); + if ((respEstimated = NSPV_getaddressutxos(&U, coinaddr, isCC, skipcount, maxrecords)) > 0) { + response.resize(nspvHeaderSize + respEstimated); response[0] = NSPV_UTXOSRESP; - if ( NSPV_rwutxosresp(IGUANA_WRITE, &response[1], &U) == slen ) - { - pfrom->PushMessage("nSPV",response); - pfrom->prevtimes[ind] = timestamp; + memcpy(&response[1], &requestId, sizeof(requestId)); + int32_t respWritten = NSPV_rwutxosresp(IGUANA_WRITE, &response[nspvHeaderSize], &U); + if (respWritten > 0 && respWritten <= respEstimated) { + response.resize(nspvHeaderSize + respWritten); + pfrom->PushMessage("nSPV", response); + pfrom->nspvdata[idata].prevtime = timestamp; + pfrom->nspvdata[idata].nreqs++; LogPrint("nspv-details", "NSPV_UTXOS response: numutxos=%d to node=%d\n", U.numutxos, pfrom->id); + } else { + LogPrint("nspv", "NSPV_rwutxosresp incorrect written response len.%d\n", respWritten); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_RESPONSE); } - else - LogPrint("nspv", "NSPV_rwutxosresp incorrect response len.%d\n", slen); NSPV_utxosresp_purge(&U); + } else { + LogPrint("nspv", "NSPV_getaddressutxos error respEstimated.%d\n", respEstimated); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_READ_DATA); } - else - LogPrint("nspv", "NSPV_getaddressutxos error.%d\n", slen); - } + } break; - case NSPV_TXIDS: + case NSPV_TXIDS: + case NSPV_TXIDS_V2: { struct NSPV_txidsresp T; - char coinaddr[KOMODO_ADDRESS_BUFSIZE]; - int32_t skipcount = 0; - uint32_t filter = 0; + char coinaddr[KOMODO_ADDRESS_BUFSIZE]; + int32_t param1 = 0; + int32_t param2 = 0; uint8_t isCC = 0; - //fprintf(stderr,"utxos: %u > %u, ind.%d, len.%d\n",timestamp,pfrom->prevtimes[ind],ind,len); - - if (len < 2) { - LogPrint("nspv", "NSPV_TXIDS bad request too short len.%d, node %d\n", len, pfrom->id); + int32_t respEstimated; + + if (requestDataLen < 1) { + LogPrint("nspv", "NSPV_TXIDS[_V2] bad request too short len.%d, node %d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); return; } - - int32_t offset = sizeof(request[0]) + sizeof(request[1]); - int32_t addrlen = request[1]; - if (offset + addrlen > len || addrlen > sizeof(coinaddr)-1) // out of bounds + + int32_t addrlen = requestData[0]; + int32_t offset = 1; + if (offset + addrlen > requestDataLen || addrlen > sizeof(coinaddr) - 1) // out of bounds { - LogPrint("nspv", "NSPV_TXIDS bad request len.%d too short or addrlen.%d out of bounds, node=%d\n", len, addrlen, pfrom->id); + LogPrint("nspv", "NSPV_TXIDS[_V2] bad request len.%d too short or addrlen.%d out of bounds, node=%d\n", requestDataLen, addrlen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); return; } - memcpy(coinaddr, &request[offset], addrlen); - coinaddr[addrlen] = 0; + memcpy(coinaddr, &requestData[offset], addrlen); + coinaddr[addrlen] = '\0'; offset += addrlen; - if (offset + sizeof(isCC) <= len) // TODO: a bit different from others format - allows omitted params, maybe better have fixed + if (offset + sizeof(isCC) <= requestDataLen) // TODO: a bit different from others format - allows omitted params, maybe better have fixed { - isCC = (request[offset] != 0); + isCC = (requestData[offset] != 0); offset += sizeof(isCC); - if (offset + sizeof(skipcount) <= len) - { - iguana_rwnum(IGUANA_READ, &request[offset], sizeof(skipcount), &skipcount); - offset += sizeof(skipcount); - if (offset + sizeof(filter) <= len) - { - iguana_rwnum(IGUANA_READ, &request[offset], sizeof(filter), &filter); - offset += sizeof(filter); + if (offset + sizeof(param1) <= requestDataLen) { + iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(param1), ¶m1); + offset += sizeof(param1); + if (offset + sizeof(param2) <= requestDataLen) { + iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(param2), ¶m2); + offset += sizeof(param2); } } } - if (offset != len) { - LogPrint("nspv", "NSPV_TXIDS bad request parameters format: len.%d, offset.%d, addrlen.%d, node=%d\n", len, offset, addrlen, pfrom->id); + if (offset != requestDataLen) { + LogPrint("nspv", "NSPV_TXIDS[_V2] bad request parameters format: len.%d, offset.%d, addrlen.%d, node=%d\n", requestDataLen, offset, addrlen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); return; } - LogPrint("nspv-details", "NSPV_TXIDS address=%s isCC.%d skipcount.%d filter.%x\n",coinaddr, isCC, skipcount, filter); + LogPrint("nspv-details", "NSPV_TXIDS[_V2] requestType=0x%02x address=%s isCC.%d param1.%d param2.%d\n", (int)requestType, coinaddr, isCC, param1, param2); memset(&T, 0, sizeof(T)); - if ((slen = NSPV_getaddresstxids(&T, coinaddr, isCC, skipcount, filter)) > 0) - { - //fprintf(stderr,"slen.%d\n",slen); - response.resize(1 + slen); - response[0] = NSPV_TXIDSRESP; - if (NSPV_rwtxidsresp(IGUANA_WRITE, &response[1], &T) == slen) - { - pfrom->PushMessage("nSPV",response); - pfrom->prevtimes[ind] = timestamp; - LogPrint("nspv-details", "NSPV_TXIDS response: numtxids=%d to node=%d\n", (int)T.numtxids, pfrom->id); + if ((respEstimated = NSPV_getaddresstxids(&T, coinaddr, isCC, (requestType == NSPV_TXIDS), param1, param2)) > 0) { + response.resize(nspvHeaderSize + respEstimated); + response[0] = requestType == NSPV_TXIDS ? NSPV_TXIDSRESP : NSPV_TXIDSRESP_V2; + memcpy(&response[1], &requestId, sizeof(requestId)); + int32_t respWritten = NSPV_rwtxidsresp(IGUANA_WRITE, &response[nspvHeaderSize], &T); + if (respWritten > 0 && respWritten <= respEstimated) { + response.resize(nspvHeaderSize + respWritten); + pfrom->PushMessage("nSPV", response); + pfrom->nspvdata[idata].prevtime = timestamp; + pfrom->nspvdata[idata].nreqs++; + LogPrint("nspv-details", "NSPV_TXIDS[_V2] response: numtxids=%d to node=%d\n", (int)T.numtxids, pfrom->id); + } else { + LogPrint("nspv", "NSPV_TXIDS[_V2] NSPV_rwtxidsresp incorrect response written len.%d\n", respWritten); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_RESPONSE); } - else - LogPrint("nspv", "NSPV_rwtxidsresp incorrect response len.%d\n", slen); NSPV_txidsresp_purge(&T); + } else { + LogPrint("nspv", "NSPV_TXIDS[_V2] NSPV_getaddresstxids error.%d\n", respEstimated); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_READ_DATA); } - else - LogPrint("nspv", "NSPV_getaddresstxids error.%d\n", slen); - } + } break; case NSPV_MEMPOOL: @@ -1075,231 +1254,304 @@ void komodo_nSPVreq(CNode *pfrom,std::vector request) // received a req uint256 txid; uint8_t funcid, isCC = 0; int8_t addrlen; - if (len > 1 + sizeof(isCC) + sizeof(funcid) + sizeof(vout) + sizeof(txid) + sizeof(addrlen)) - { - uint32_t offset = 1; - offset += iguana_rwnum(IGUANA_READ, &request[offset], sizeof(isCC), &isCC); - offset += iguana_rwnum(IGUANA_READ, &request[offset], sizeof(funcid), &funcid); - offset += iguana_rwnum(IGUANA_READ, &request[offset], sizeof(vout), &vout); - offset += iguana_rwbignum(IGUANA_READ, &request[offset], sizeof(txid), (uint8_t*)&txid); - addrlen = request[offset++]; - if (addrlen < sizeof(coinaddr) && offset + addrlen == len) - { - memcpy(coinaddr, &request[offset], addrlen); + int32_t respEstimated; + + NSPV_senderror(pfrom, requestId, NSPV_ERROR_DEPRECATED); + + if (requestDataLen > sizeof(isCC) + sizeof(funcid) + sizeof(vout) + sizeof(txid) + sizeof(addrlen)) { + uint32_t offset = 0; + offset += iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(isCC), &isCC); + offset += iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(funcid), &funcid); + offset += iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(vout), &vout); + offset += iguana_rwbignum(IGUANA_READ, &requestData[offset], sizeof(txid), (uint8_t*)&txid); + addrlen = requestData[offset++]; + if (addrlen < sizeof(coinaddr) && offset + addrlen == requestDataLen) { + memcpy(coinaddr, &requestData[offset], addrlen); coinaddr[addrlen] = 0; offset += addrlen; - LogPrint("nspv-details", "address (%s) isCC.%d funcid.%d %s/v%d len.%d slen.%d\n", coinaddr, isCC, funcid, txid.GetHex().c_str(), vout, len, addrlen); + LogPrint("nspv-details", "address (%s) isCC.%d funcid.%d %s/v%d len.%d addrlen.%d\n", coinaddr, isCC, funcid, txid.GetHex().c_str(), vout, requestDataLen, addrlen); memset(&M, 0, sizeof(M)); - if ((slen = NSPV_mempooltxids(&M, coinaddr, isCC, funcid, txid, vout)) > 0) { - //fprintf(stderr,"NSPV_mempooltxids slen.%d\n",slen); - response.resize(1 + slen); + if ((respEstimated = NSPV_mempooltxids(&M, coinaddr, isCC, funcid, txid, vout)) > 0) { + //fprintf(stderr,"NSPV_mempooltxids respLen.%d\n",respLen); + response.resize(nspvHeaderSize + respEstimated); response[0] = NSPV_MEMPOOLRESP; - if (NSPV_rwmempoolresp(IGUANA_WRITE, &response[1], &M) == slen) { + memcpy(&response[1], &requestId, sizeof(requestId)); + int32_t respWritten = NSPV_rwmempoolresp(IGUANA_WRITE, &response[nspvHeaderSize], &M); + if (respWritten > 0 && respWritten <= respEstimated) { + response.resize(nspvHeaderSize + respWritten); pfrom->PushMessage("nSPV", response); - pfrom->prevtimes[ind] = timestamp; + pfrom->nspvdata[idata].prevtime = timestamp; + pfrom->nspvdata[idata].nreqs++; LogPrint("nspv-details", "NSPV_MEMPOOL response: numtxids=%d to node=%d\n", M.numtxids, pfrom->id); - } else - LogPrint("nspv", "NSPV_rwmempoolresp incorrect response len.%d\n", slen); + } else { + LogPrint("nspv", "NSPV_rwmempoolresp incorrect response written len.%d\n", respWritten); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_RESPONSE); + } NSPV_mempoolresp_purge(&M); - } - else - LogPrint("nspv", "NSPV_mempooltxids err.%d\n", slen); - } - else - LogPrint("nspv", "NSPV_MEMPOOL incorrect addrlen.%d offset.%d len.%d\n", addrlen, offset, len); - } - else - LogPrint("nspv", "NSPV_MEMPOOL incorrect len.%d too short, node %d\n", len, pfrom->id); + } else { + LogPrint("nspv", "NSPV_mempooltxids err.%d\n", respEstimated); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_READ_DATA); + } + } else { + LogPrint("nspv", "NSPV_MEMPOOL incorrect addrlen.%d offset.%d len.%d\n", addrlen, offset, requestDataLen); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); + } + } else { + LogPrint("nspv", "NSPV_MEMPOOL incorrect len.%d too short, node %d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); + } } break; - case NSPV_NTZS: + case NSPV_NTZS: { - struct NSPV_ntzsresp N; + struct NSPV_ntzsresp N; int32_t height; - if (len == 1 + sizeof(height)) - { - iguana_rwnum(IGUANA_READ, &request[1], sizeof(height), &height); + if (requestDataLen == sizeof(height)) { + int32_t respEstimated; + + iguana_rwnum(IGUANA_READ, &requestData[0], sizeof(height), &height); memset(&N, 0, sizeof(N)); - if ((slen = NSPV_getntzsresp(&N, height)) > 0) - { - response.resize(1 + slen); + if ((respEstimated = NSPV_getntzsresp(&N, height)) > 0) { + response.resize(nspvHeaderSize + respEstimated); response[0] = NSPV_NTZSRESP; - if (NSPV_rwntzsresp(IGUANA_WRITE, &response[1], &N) == slen) - { - pfrom->PushMessage("nSPV",response); - pfrom->prevtimes[ind] = timestamp; - LogPrint("nspv-details", "NSPV_NTZS response: prevntz.txid=%s nextntx.txid=%s node=%d\n", N.prevntz.txid.GetHex().c_str(), N.nextntz.txid.GetHex().c_str(), pfrom->id); + memcpy(&response[1], &requestId, sizeof(requestId)); + int32_t respWritten = NSPV_rwntzsresp(IGUANA_WRITE, &response[nspvHeaderSize], &N); + if (respWritten > 0 && respWritten <= respEstimated) { + response.resize(nspvHeaderSize + respWritten); + pfrom->PushMessage("nSPV", response); + pfrom->nspvdata[idata].prevtime = timestamp; + pfrom->nspvdata[idata].nreqs++; + LogPrint("nspv-details", "NSPV_NTZS response: ntz.txid=%s node=%d\n", N.ntz.txid.GetHex(), pfrom->id); + } else { + LogPrint("nspv", "NSPV_rwntzsresp incorrect response written len.%d\n", respWritten); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_RESPONSE); } - else - LogPrint("nspv", "NSPV_rwntzsresp incorrect response len.%d\n", slen); NSPV_ntzsresp_purge(&N); + } else { + LogPrint("nspv", "NSPV_rwntzsresp respLen err.%d\n", respEstimated); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_READ_DATA); } - else - LogPrint("nspv", "NSPV_rwntzsresp err.%d\n", slen); - } else - LogPrint("nspv","NSPV_NTZS bad request len.%d node %d\n", len, pfrom->id); - } + } else { + LogPrint("nspv", "NSPV_NTZS bad request len.%d node %d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); + } + } break; - case NSPV_NTZSPROOF: + case NSPV_NTZSPROOF: { - struct NSPV_ntzsproofresp P; - uint256 prevntz, nextntz; - if (len == 1 + sizeof(prevntz) + sizeof(nextntz)) - { - iguana_rwbignum(IGUANA_READ, &request[1], sizeof(prevntz), (uint8_t*)&prevntz); - iguana_rwbignum(IGUANA_READ, &request[1 + sizeof(prevntz)], sizeof(nextntz), (uint8_t*)&nextntz); + struct NSPV_ntzsproofresp P; + uint256 ntztxid; + if (requestDataLen == sizeof(ntztxid)) { + int32_t respEstimated; + + iguana_rwbignum(IGUANA_READ, &requestData[0], sizeof(ntztxid), (uint8_t*)&ntztxid); memset(&P, 0, sizeof(P)); - if ((slen = NSPV_getntzsproofresp(&P, prevntz, nextntz)) > 0) - { - // fprintf(stderr,"slen.%d msg prev.%s next.%s\n",slen,prevntz.GetHex().c_str(),nextntz.GetHex().c_str()); - response.resize(1 + slen); + if ((respEstimated = NSPV_getntzsproofresp(&P, ntztxid)) > 0) { + // fprintf(stderr,"respLen.%d msg prev.%s next.%s\n",respLen,prevntz.GetHex().c_str(),nextntz.GetHex().c_str()); + response.resize(nspvHeaderSize + respEstimated); response[0] = NSPV_NTZSPROOFRESP; - if (NSPV_rwntzsproofresp(IGUANA_WRITE, &response[1], &P) == slen) - { + memcpy(&response[1], &requestId, sizeof(requestId)); + int32_t respWritten = NSPV_rwntzsproofresp(IGUANA_WRITE, &response[nspvHeaderSize], &P); + if (respWritten > 0) { + response.resize(nspvHeaderSize + respWritten); pfrom->PushMessage("nSPV", response); - pfrom->prevtimes[ind] = timestamp; - LogPrint("nspv-details", "NSPV_NTZSPROOF response: prevtxidht=%d nexttxidht=%d node=%d\n", P.prevtxidht, P.nexttxidht, pfrom->id); + pfrom->nspvdata[idata].prevtime = timestamp; + pfrom->nspvdata[idata].nreqs++; + LogPrint("nspv-details", "NSPV_NTZSPROOF response: nexttxidht=%d node=%d\n", P.nexttxidht, pfrom->id); + } else { + LogPrint("nspv", "NSPV_rwntzsproofresp incorrect response written len.%d\n", respWritten); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_RESPONSE); } - else - LogPrint("nspv", "NSPV_rwntzsproofresp incorrect response len.%d\n", slen); NSPV_ntzsproofresp_purge(&P); - } - else - LogPrint("nspv", "NSPV_NTZSPROOF err.%d\n", slen); + } else { + LogPrint("nspv", "NSPV_NTZSPROOF respLen err.%d\n", respEstimated); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_READ_DATA); + } + } else { + LogPrint("nspv", "NSPV_NTZSPROOF bad request len.%d node %d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); } - else - LogPrint("nspv","NSPV_NTZSPROOF bad request len.%d node %d\n", len, pfrom->id); - } + } break; case NSPV_TXPROOF: { - struct NSPV_txproof P; - uint256 txid; + struct NSPV_txproof P; + uint256 txid; int32_t height, vout; - if (len == 1 + sizeof(txid) + sizeof(height) + sizeof(vout)) - { - iguana_rwnum(IGUANA_READ, &request[1], sizeof(height), &height); - iguana_rwnum(IGUANA_READ, &request[1 + sizeof(height)], sizeof(vout), &vout); - iguana_rwbignum(IGUANA_READ, &request[1 + sizeof(height) + sizeof(vout)], sizeof(txid), (uint8_t*)&txid); + if (requestDataLen == sizeof(txid) + sizeof(height) + sizeof(vout)) { + int32_t respEstimated; + + iguana_rwnum(IGUANA_READ, &requestData[0], sizeof(height), &height); + iguana_rwnum(IGUANA_READ, &requestData[sizeof(height)], sizeof(vout), &vout); + iguana_rwbignum(IGUANA_READ, &requestData[sizeof(height) + sizeof(vout)], sizeof(txid), (uint8_t*)&txid); //fprintf(stderr,"got txid %s/v%d ht.%d\n",txid.GetHex().c_str(),vout,height); memset(&P, 0, sizeof(P)); - if ((slen = NSPV_gettxproof(&P,vout,txid,height)) > 0) - { - //fprintf(stderr,"slen.%d\n",slen); - response.resize(1 + slen); + if ((respEstimated = NSPV_gettxproof(&P, vout, txid /*,height*/)) > 0) { + //fprintf(stderr,"respEstimated.%d\n",respEstimated); + response.resize(nspvHeaderSize + respEstimated); response[0] = NSPV_TXPROOFRESP; - if (NSPV_rwtxproof(IGUANA_WRITE, &response[1], &P) == slen) - { + memcpy(&response[1], &requestId, sizeof(requestId)); + int32_t respWritten = NSPV_rwtxproof(IGUANA_WRITE, &response[nspvHeaderSize], &P); + if (respWritten > 0 && respWritten <= respEstimated) { + response.resize(nspvHeaderSize + respWritten); //fprintf(stderr,"send response\n"); - pfrom->PushMessage("nSPV",response); - pfrom->prevtimes[ind] = timestamp; + pfrom->PushMessage("nSPV", response); + pfrom->nspvdata[idata].prevtime = timestamp; + pfrom->nspvdata[idata].nreqs++; LogPrint("nspv-details", "NSPV_TXPROOF response: txlen=%d txprooflen=%d node=%d\n", P.txlen, P.txprooflen, pfrom->id); + } else { + LogPrint("nspv", "NSPV_rwtxproof incorrect response written len.%d\n", respWritten); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_RESPONSE); } - else - LogPrint("nspv", "NSPV_rwtxproof incorrect response len.%d\n", slen); NSPV_txproof_purge(&P); - } - else - LogPrint("nspv", "gettxproof error.%d\n", slen); - } - else - LogPrint("nspv","txproof bad request len.%d node %d\n", len, pfrom->id); - } + } else { + LogPrint("nspv", "gettxproof error.%d\n", respEstimated); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_READ_DATA); + } + } else { + LogPrint("nspv", "txproof bad request len.%d node %d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); + } + } break; - case NSPV_SPENTINFO: + case NSPV_SPENTINFO: { - struct NSPV_spentinfo S; - int32_t vout; + struct NSPV_spentinfo S; + int32_t vout; uint256 txid; - if (len == 1 + sizeof(txid) + sizeof(vout)) - { - iguana_rwnum(IGUANA_READ, &request[1], sizeof(vout), &vout); - iguana_rwbignum(IGUANA_READ, &request[1 + sizeof(vout)], sizeof(txid), (uint8_t*)&txid); + if (requestDataLen == sizeof(txid) + sizeof(vout)) { + int32_t respEstimated; + + iguana_rwnum(IGUANA_READ, &requestData[0], sizeof(vout), &vout); + iguana_rwbignum(IGUANA_READ, &requestData[sizeof(vout)], sizeof(txid), (uint8_t*)&txid); memset(&S, 0, sizeof(S)); - if ((slen = NSPV_getspentinfo(&S, txid, vout)) > 0) - { - response.resize(1 + slen); + if ((respEstimated = NSPV_getspentinfo(&S, txid, vout)) > 0) { + response.resize(nspvHeaderSize + respEstimated); response[0] = NSPV_SPENTINFORESP; - if (NSPV_rwspentinfo(IGUANA_WRITE, &response[1], &S) == slen) - { - pfrom->PushMessage("nSPV",response); - pfrom->prevtimes[ind] = timestamp; - LogPrint("nspv-details", "NSPV_SPENTINFO response: spent txid=%s vout=%d node=%d\n", S.txid.GetHex().c_str(), S.vout, pfrom->id); + memcpy(&response[1], &requestId, sizeof(requestId)); + int32_t respWritten = NSPV_rwspentinfo(IGUANA_WRITE, &response[nspvHeaderSize], &S); + if (respWritten > 0 && respWritten <= respEstimated) { + response.resize(nspvHeaderSize + respWritten); + pfrom->PushMessage("nSPV", response); + pfrom->nspvdata[idata].prevtime = timestamp; + pfrom->nspvdata[idata].nreqs++; + LogPrint("nspv-details", "NSPV_SPENTINFO response: spending txid=%s vini=%d node=%d\n", S.spent.txid.GetHex(), S.spentvini, pfrom->id); + } else { + LogPrint("nspv", "NSPV_rwspentinfo incorrect response written len.%d\n", respWritten); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_RESPONSE); } - else - LogPrint("nspv", "NSPV_rwspentinfo incorrect response len.%d\n", slen); NSPV_spentinfo_purge(&S); + } else { + LogPrint("nspv", "NSPV_getspentinfo error.%d node=%d\n", respEstimated, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_READ_DATA); } - else - LogPrint("nspv", "NSPV_getspentinfo error.%d node=%d\n", slen, pfrom->id); + } else { + LogPrint("nspv", "NSPV_SPENTINFO bad request len.%d node=%d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); } - else - LogPrint("nspv", "NSPV_SPENTINFO bad request len.%d node=%d\n", len, pfrom->id); - } + } break; - case NSPV_BROADCAST: + case NSPV_BROADCAST: { - struct NSPV_broadcastresp B; + struct NSPV_broadcastresp B; uint256 txid; int32_t txlen; - if (len > 1 + sizeof(txid) + sizeof(txlen)) - { - int32_t offset = 1; + if (requestDataLen > sizeof(txid) + sizeof(txlen)) { + int32_t offset = 0; + int32_t respEstimated; + CValidationState state; - offset += iguana_rwbignum(IGUANA_READ, &request[offset], sizeof(txid), (uint8_t*)&txid); - offset += iguana_rwnum(IGUANA_READ, &request[offset], sizeof(txlen), &txlen); + offset += iguana_rwbignum(IGUANA_READ, &requestData[offset], sizeof(txid), (uint8_t*)&txid); + offset += iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(txlen), &txlen); memset(&B, 0, sizeof(B)); - if (txlen < MAX_TX_SIZE_AFTER_SAPLING && request.size() == offset + txlen && (slen = NSPV_sendrawtransaction(&B, &request[offset], txlen)) > 0) - { - response.resize(1 + slen); - response[0] = NSPV_BROADCASTRESP; - if (NSPV_rwbroadcastresp(IGUANA_WRITE, &response[1], &B) == slen) - { - pfrom->PushMessage("nSPV",response); - pfrom->prevtimes[ind] = timestamp; - LogPrint("nspv-details", "NSPV_BROADCAST response: txid=%s vout=%d to node=%d\n", B.txid.GetHex().c_str(), pfrom->id); + if (txlen < MAX_TX_SIZE_AFTER_SAPLING && requestDataLen == offset + txlen && (respEstimated = NSPV_sendrawtransaction(&B, &requestData[offset], txlen, &state)) > 0) { + if (B.retcode >= 0) { + response.resize(nspvHeaderSize + respEstimated); + response[0] = NSPV_BROADCASTRESP; + memcpy(&response[1], &requestId, sizeof(requestId)); + + int32_t respWritten = NSPV_rwbroadcastresp(IGUANA_WRITE, &response[nspvHeaderSize], &B); + if (respWritten > 0 && respWritten <= respEstimated) { + response.resize(nspvHeaderSize + respWritten); + pfrom->PushMessage("nSPV", response); + pfrom->nspvdata[idata].prevtime = timestamp; + pfrom->nspvdata[idata].nreqs++; + LogPrint("nspv-details", "NSPV_BROADCAST response: txid=%s vout=%d to node=%d\n", B.txid.GetHex().c_str(), pfrom->id); + } else { + LogPrint("nspv", "NSPV_rwbroadcastresp incorrect response written len.%d\n", respWritten); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_RESPONSE); + } + } + else { + std::string errDesc = "unknown-error"; + if (B.retcode == -1) + errDesc = "tx-decode-failed"; + else if (B.retcode == -3) + errDesc = strprintf("add-to-mempool-error: validation code=%d reason=%s", state.GetRejectCode(), state.GetRejectReason()); + else if (B.retcode == -4) + errDesc = "add-to-mempool-error: missing inputs"; + NSPV_senderror(pfrom, requestId, NSPV_ERROR_BROADCAST, errDesc); + LogPrint("nspv", "NSPV_sendrawtransaction error retcode %d state code=%d reason=%s node=%d\n", B.retcode, state.GetRejectCode(), state.GetRejectReason(), pfrom->id); } - else - LogPrint("nspv", "NSPV_rwbroadcastresp incorrect response len.%d\n", slen); NSPV_broadcast_purge(&B); - } - else - LogPrint("nspv", "NSPV_BROADCAST either wrong tx len.%d or NSPV_sendrawtransaction error.%d, node=%d\n", txlen, slen, pfrom->id); + } else { + LogPrint("nspv", "NSPV_BROADCAST either wrong tx len.%d or NSPV_sendrawtransaction error.%d, node=%d\n", txlen, respEstimated, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_BROADCAST); + } + } else { + LogPrint("nspv", "NSPV_BROADCAST bad request len.%d node %d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); } - else - LogPrint("nspv", "NSPV_BROADCAST bad request len.%d node %d\n", len, pfrom->id); - - } + } break; - case NSPV_REMOTERPC: + case NSPV_REMOTERPC: { - struct NSPV_remoterpcresp R; - int32_t offset = 1; - offset += iguana_rwnum(IGUANA_READ, &request[offset], sizeof(slen), &slen); + struct NSPV_remoterpcresp R; + int32_t offset = 0; + int32_t reqJsonLen; + int32_t respEstimated; + offset += iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(reqJsonLen), &reqJsonLen); + if (reqJsonLen > NSPV_MAXJSONREQUESTSIZE) { + LogPrint("nspv", "NSPV_REMOTERPC too big json request len.%d\n", reqJsonLen); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); + return; + } memset(&R, 0, sizeof(R)); - if (len == (offset + slen) && (slen = NSPV_remoterpc(&R,(char *)&request[offset],slen))>0 ) - { - response.resize(1 + slen); + if (requestDataLen != (offset + reqJsonLen)) { + LogPrint("nspv", "NSPV_REMOTERPC bad request len.%d node %d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); + } + if ((respEstimated = NSPV_remoterpc(&R, (char*)&requestData[offset], reqJsonLen)) > 0) { + response.resize(nspvHeaderSize + respEstimated); response[0] = NSPV_REMOTERPCRESP; - NSPV_rwremoterpcresp(IGUANA_WRITE, &response[1], &R, slen); - pfrom->PushMessage("nSPV", response); - pfrom->prevtimes[ind] = timestamp; - LogPrint("nspv-details", "NSPV_REMOTERPCRESP response: method=%s json=%s to node=%d\n", R.method, R.json, pfrom->id); + memcpy(&response[1], &requestId, sizeof(requestId)); + int32_t respWritten = NSPV_rwremoterpcresp(IGUANA_WRITE, &response[nspvHeaderSize], &R, respEstimated); + if (respWritten > 0 && respWritten <= respEstimated) { + response.resize(nspvHeaderSize + respWritten); + pfrom->PushMessage("nSPV", response); + pfrom->nspvdata[idata].prevtime = timestamp; + pfrom->nspvdata[idata].nreqs++; + LogPrint("nspv-details", "NSPV_REMOTERPCRESP response: method=%s json=%s to node=%d\n", R.method, R.json, pfrom->id); + } else { + LogPrint("nspv", "NSPV_rwbroadcastresp incorrect response written len.%d\n", respWritten); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_RESPONSE); + } NSPV_remoterpc_purge(&R); + } else { + LogPrint("nspv", "NSPV_REMOTERPC could not execute request node %d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_REMOTE_RPC); } - else - LogPrint("nspv", "NSPV_REMOTERPC bad request len.%d node %d\n", len, pfrom->id); - } + } break; - - case NSPV_CCMODULEUTXOS: // get cc module utxos from coinaddr for the requested amount, evalcode, funcid list and txid + + case NSPV_CCMODULEUTXOS: // get cc module utxos from coinaddr for the requested amount, evalcode, funcid list and txid { struct NSPV_utxosresp U; char coinaddr[64]; @@ -1309,66 +1561,81 @@ void komodo_nSPVreq(CNode *pfrom,std::vector request) // received a req char funcids[27]; uint256 filtertxid; bool errorFormat = false; - //fprintf(stderr,"utxos: %u > %u, ind.%d, len.%d\n",timestamp,pfrom->prevtimes[ind],ind,len); - - if (len < 3) { - LogPrint("nspv", "NSPV_CCMODULEUTXOS bad request len.%d too short, node=%d\n", len, pfrom->id); + int32_t respEstimated; + + NSPV_senderror(pfrom, requestId, NSPV_ERROR_DEPRECATED); + //fprintf(stderr,"utxos: %u > %u, ind.%d, len.%d\n",timestamp,pfrom->nspvdata[ind].prevtime,ind,len); + + if (requestDataLen < sizeof(int32_t)) { + LogPrint("nspv", "NSPV_CCMODULEUTXOS bad request len.%d too short, node=%d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); return; } - - int32_t offset = 1; - int32_t addrlen = request[offset++]; - if (addrlen >= sizeof(coinaddr) || offset + addrlen > len) - { - LogPrint("nspv", "NSPV_CCMODULEUTXOS bad request len.%d too short or addrlen %d too long, node=%d\n", len, addrlen, pfrom->id); + + int32_t offset = 0; + int32_t addrlen = requestData[offset++]; + if (addrlen >= sizeof(coinaddr) || offset + addrlen > requestDataLen) { + LogPrint("nspv", "NSPV_CCMODULEUTXOS bad request len.%d too short or addrlen %d too long, node=%d\n", requestDataLen, addrlen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); return; } - memcpy(coinaddr, &request[offset], addrlen); + memcpy(coinaddr, &requestData[offset], addrlen); coinaddr[addrlen] = 0; offset += addrlen; - if (offset + sizeof(amount) + sizeof(evalcode) + sizeof(funcidslen) > len) { - LogPrint("nspv", "NSPV_CCMODULEUTXOS bad request len.%d too short, node=%d\n", len, pfrom->id); + if (offset + sizeof(amount) + sizeof(evalcode) + sizeof(funcidslen) > requestDataLen) { + LogPrint("nspv", "NSPV_CCMODULEUTXOS bad request len.%d too short, node=%d\n", requestDataLen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); return; } - offset += iguana_rwnum(IGUANA_READ, &request[offset], sizeof(amount), &amount); - offset += iguana_rwnum(IGUANA_READ, &request[offset], sizeof(evalcode), &evalcode); - funcidslen = request[offset++]; + offset += iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(amount), &amount); + offset += iguana_rwnum(IGUANA_READ, &requestData[offset], sizeof(evalcode), &evalcode); + funcidslen = requestData[offset++]; - if (funcidslen >= sizeof(funcids) || offset + funcidslen > len) - { - LogPrint("nspv", "NSPV_CCMODULEUTXOS bad request len.%d no room for funcids or too many funcids %d, node=%d\n", len, funcidslen, pfrom->id); + if (funcidslen >= sizeof(funcids) || offset + funcidslen > requestDataLen) { + LogPrint("nspv", "NSPV_CCMODULEUTXOS bad request len.%d no room for funcids or too many funcids %d, node=%d\n", requestDataLen, funcidslen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); return; } - memcpy(funcids, &request[offset], funcidslen); + memcpy(funcids, &requestData[offset], funcidslen); funcids[funcidslen] = 0; offset += funcidslen; - if (offset + sizeof(filtertxid) != len) { - LogPrint("nspv", "NSPV_CCMODULEUTXOS bad request len.%d incorrect room for filtertxid param, node=%d\n", len, funcidslen, pfrom->id); + if (offset + sizeof(filtertxid) != requestDataLen) { + LogPrint("nspv", "NSPV_CCMODULEUTXOS bad request len.%d incorrect room for filtertxid param, node=%d\n", requestDataLen, funcidslen, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_DATA); return; } - iguana_rwbignum(IGUANA_READ, &request[offset], sizeof(filtertxid), (uint8_t *)&filtertxid); + iguana_rwbignum(IGUANA_READ, &requestData[offset], sizeof(filtertxid), (uint8_t*)&filtertxid); memset(&U, 0, sizeof(U)); - if ((slen = NSPV_getccmoduleutxos(&U, coinaddr, amount, evalcode, funcids, filtertxid)) > 0) - { - response.resize(1 + slen); + if ((respEstimated = NSPV_getccmoduleutxos(&U, coinaddr, amount, evalcode, funcids, filtertxid)) > 0) { + response.resize(nspvHeaderSize + respEstimated); response[0] = NSPV_CCMODULEUTXOSRESP; - if (NSPV_rwutxosresp(IGUANA_WRITE, &response[1], &U) == slen) - { + memcpy(&response[1], &requestId, sizeof(requestId)); + int32_t respWritten = NSPV_rwutxosresp(IGUANA_WRITE, &response[nspvHeaderSize], &U); + if (respWritten > 0 && respWritten <= respEstimated) { + response.resize(nspvHeaderSize + respWritten); pfrom->PushMessage("nSPV", response); - pfrom->prevtimes[ind] = timestamp; + pfrom->nspvdata[idata].prevtime = timestamp; + pfrom->nspvdata[idata].nreqs++; LogPrint("nspv-details", "NSPV_CCMODULEUTXOS returned %d utxos to node=%d\n", (int)U.numutxos, pfrom->id); + } else { + LogPrint("nspv", "NSPV_rwutxosresp incorrect response written len.%d\n", respWritten); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_RESPONSE); } - else - LogPrint("nspv", "NSPV_rwutxosresp incorrect response len.%d\n", slen); NSPV_utxosresp_purge(&U); + } else { + LogPrint("nspv", "NSPV_getccmoduleutxos error.%d, node %d\n", respEstimated, pfrom->id); + NSPV_senderror(pfrom, requestId, NSPV_ERROR_READ_DATA); } - else - LogPrint("nspv", "NSPV_getccmoduleutxos error.%d, node %d\n", slen, pfrom->id); - } + + } + break; + + default: + NSPV_senderror(pfrom, requestId, NSPV_ERROR_INVALID_REQUEST_TYPE); break; } } diff --git a/src/komodo_nSPV_superlite.h b/src/komodo_nSPV_superlite.h index 0977b59ab1d..a9310c20113 100644 --- a/src/komodo_nSPV_superlite.h +++ b/src/komodo_nSPV_superlite.h @@ -17,6 +17,16 @@ #ifndef KOMODO_NSPVSUPERLITE_H #define KOMODO_NSPVSUPERLITE_H +#include + +#include "key_io.h" +#include "main.h" +#include "komodo_defs.h" +#include "notarisationdb.h" +#include "rpc/server.h" +#include "cc/CCinclude.h" +#include "komodo_nSPV_defs.h" +#include "komodo_nSPV.h" #include "komodo_DEX.h" // nSPV client. VERY simplistic "single threaded" networking model. for production GUI best to multithread, etc. @@ -107,11 +117,11 @@ struct NSPV_txproof *NSPV_txproof_add(struct NSPV_txproof *ptr) return(&NSPV_txproof_cache[i]); } -struct NSPV_ntzsproofresp *NSPV_ntzsproof_find(uint256 prevtxid,uint256 nexttxid) +struct NSPV_ntzsproofresp *NSPV_ntzsproof_find(uint256 nexttxid) { int32_t i; for (i=0; iprevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str()); + fprintf(stderr,"ADD CACHE ntzsproof %s\n", ptr->nexttxid.GetHex().c_str()); return(&NSPV_ntzsproofresp_cache[i]); } @@ -179,26 +189,26 @@ void komodo_nSPVresp(CNode *pfrom,std::vector response) // received a r NSPV_rwntzsresp(0,&response[1],&NSPV_ntzsresult); if ( NSPV_ntzsresp_find(NSPV_ntzsresult.reqheight) == 0 ) NSPV_ntzsresp_add(&NSPV_ntzsresult); - fprintf(stderr,"got ntzs response %u size.%d %s prev.%d, %s next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.prevntz.height,NSPV_ntzsresult.nextntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.height); + fprintf(stderr,"got ntzs response %u size.%d ntz.txid %s ntzed.height.%d\n",timestamp,(int32_t)response.size(), NSPV_ntzsresult.ntz.txid.GetHex().c_str(), NSPV_ntzsresult.ntz.ntzheight); break; case NSPV_NTZSPROOFRESP: NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult); NSPV_rwntzsproofresp(0,&response[1],&NSPV_ntzsproofresult); - if ( NSPV_ntzsproof_find(NSPV_ntzsproofresult.prevtxid,NSPV_ntzsproofresult.nexttxid) == 0 ) + if ( NSPV_ntzsproof_find(NSPV_ntzsproofresult.nexttxid) == 0 ) NSPV_ntzsproof_add(&NSPV_ntzsproofresult); - fprintf(stderr,"got ntzproof response %u size.%d prev.%d next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsproofresult.common.prevht,NSPV_ntzsproofresult.common.nextht); + fprintf(stderr,"got ntzproof response %u size.%d next.%d\n",timestamp,(int32_t)response.size(), NSPV_ntzsproofresult.common.nextht); break; case NSPV_TXPROOFRESP: NSPV_txproof_purge(&NSPV_txproofresult); NSPV_rwtxproof(0,&response[1],&NSPV_txproofresult); if ( NSPV_txproof_find(NSPV_txproofresult.txid) == 0 ) NSPV_txproof_add(&NSPV_txproofresult); - fprintf(stderr,"got txproof response %u size.%d %s ht.%d\n",timestamp,(int32_t)response.size(),NSPV_txproofresult.txid.GetHex().c_str(),NSPV_txproofresult.height); + fprintf(stderr,"got txproof response %u size.%d %s ht.%d\n",timestamp,(int32_t)response.size(), NSPV_txproofresult.txid.GetHex().c_str(),NSPV_txproofresult.height); break; case NSPV_SPENTINFORESP: NSPV_spentinfo_purge(&NSPV_spentresult); NSPV_rwspentinfo(0,&response[1],&NSPV_spentresult); - fprintf(stderr,"got spentinfo response %u size.%d\n",timestamp,(int32_t)response.size()); + fprintf(stderr,"got spentinfo response %u size.%d\n", timestamp,(int32_t)response.size()); break; case NSPV_BROADCASTRESP: NSPV_broadcast_purge(&NSPV_broadcastresult); @@ -231,17 +241,17 @@ CNode *NSPV_req(CNode *pnode,uint8_t *msg,int32_t len,uint64_t mask,int32_t ind) n = 0; BOOST_FOREACH(CNode *ptr,vNodes) { - if ( ptr->prevtimes[ind] > timestamp ) - ptr->prevtimes[ind] = 0; + if ( ptr->nspvdata[ind].prevtime > timestamp ) + ptr->nspvdata[ind].prevtime = 0; if ( ptr->hSocket == INVALID_SOCKET ) continue; - if ( (ptr->nServices & mask) == mask && timestamp > ptr->prevtimes[ind] ) + if ( (ptr->nServices & mask) == mask && timestamp > ptr->nspvdata[ind].prevtime ) { flag = 1; pnodes[n++] = ptr; if ( n == sizeof(pnodes)/sizeof(*pnodes) ) break; - } // else fprintf(stderr,"nServices %llx vs mask %llx, t%u vs %u, ind.%d\n",(long long)ptr->nServices,(long long)mask,timestamp,ptr->prevtimes[ind],ind); + } // else fprintf(stderr,"nServices %llx vs mask %llx, t%u vs %u, ind.%d\n",(long long)ptr->nServices,(long long)mask,timestamp,ptr->nspvdata[ind].prevtime,ind); } if ( n > 0 ) pnode = pnodes[rand() % n]; @@ -254,7 +264,7 @@ CNode *NSPV_req(CNode *pnode,uint8_t *msg,int32_t len,uint64_t mask,int32_t ind) if ( (0) && KOMODO_NSPV_SUPERLITE ) fprintf(stderr,"pushmessage [%d] len.%d\n",msg[0],len); pnode->PushMessage("getnSPV",request); - pnode->prevtimes[ind] = timestamp; + pnode->nspvdata[ind].prevtime = timestamp; return(pnode); } else fprintf(stderr,"no pnodes\n"); return(0); @@ -285,11 +295,11 @@ void komodo_nSPV(CNode *pto) // polling loop from SendMessages NSPV_logout(); if ( (pto->nServices & NODE_NSPV) == 0 ) return; - if ( pto->prevtimes[NSPV_INFO>>1] > timestamp ) - pto->prevtimes[NSPV_INFO>>1] = 0; + if ( pto->nspvdata[NSPV_INFO>>1].prevtime > timestamp ) + pto->nspvdata[NSPV_INFO>>1].prevtime = 0; if ( KOMODO_NSPV_SUPERLITE ) { - if ( timestamp > NSPV_lastinfo + ASSETCHAINS_BLOCKTIME/2 && timestamp > pto->prevtimes[NSPV_INFO>>1] + 2*ASSETCHAINS_BLOCKTIME/3 ) + if ( timestamp > NSPV_lastinfo + ASSETCHAINS_BLOCKTIME/2 && timestamp > pto->nspvdata[NSPV_INFO>>1].prevtime + 2*ASSETCHAINS_BLOCKTIME/3 ) { int32_t reqht; reqht = 0; @@ -331,11 +341,11 @@ UniValue NSPV_spentinfo_json(struct NSPV_spentinfo *ptr) UniValue NSPV_ntz_json(struct NSPV_ntz *ptr) { UniValue result(UniValue::VOBJ); - result.push_back(Pair("notarized_height",(int64_t)ptr->height)); - result.push_back(Pair("notarized_blockhash",ptr->blockhash.GetHex())); + result.push_back(Pair("notarized_height",(int64_t)ptr->ntzheight)); + result.push_back(Pair("notarized_blockhash",ptr->ntzblockhash.GetHex())); result.push_back(Pair("notarization_txid",ptr->txid.GetHex())); result.push_back(Pair("notarization_txidheight",(int64_t)ptr->txidheight)); - result.push_back(Pair("notarization_desttxid",ptr->othertxid.GetHex())); + result.push_back(Pair("notarization_desttxid",ptr->desttxid.GetHex())); return(result); } @@ -376,7 +386,7 @@ UniValue NSPV_getinfo_json(struct NSPV_inforesp *ptr) } result.push_back(Pair("height",(int64_t)ptr->height)); result.push_back(Pair("chaintip",ptr->blockhash.GetHex())); - result.push_back(Pair("notarization",NSPV_ntz_json(&ptr->notarization))); + result.push_back(Pair("notarization",NSPV_ntz_json(&ptr->ntz))); result.push_back(Pair("header",NSPV_header_json(&ptr->H,ptr->hdrheight))); result.push_back(Pair("protocolversion",(int64_t)ptr->version)); result.push_back(Pair("lastpeer",NSPV_lastpeer)); @@ -395,6 +405,8 @@ UniValue NSPV_utxoresp_json(struct NSPV_utxoresp *utxos,int32_t numutxos) item.push_back(Pair("value",(double)utxos[i].satoshis/COIN)); if ( ASSETCHAINS_SYMBOL[0] == 0 ) item.push_back(Pair("interest",(double)utxos[i].extradata/COIN)); + if (utxos[i].script) + item.push_back(Pair("script", HexStr(utxos[i].script, utxos[i].script+utxos[i].script_size))); array.push_back(item); } return(array); @@ -412,23 +424,24 @@ UniValue NSPV_utxosresp_json(struct NSPV_utxosresp *ptr) result.push_back(Pair("balance",(double)ptr->total/COIN)); if ( ASSETCHAINS_SYMBOL[0] == 0 ) result.push_back(Pair("interest",(double)ptr->interest/COIN)); - result.push_back(Pair("filter",(int64_t)ptr->filter)); + result.push_back(Pair("maxrecords",(int64_t)ptr->maxrecords)); result.push_back(Pair("lastpeer",NSPV_lastpeer)); return(result); } -UniValue NSPV_txidresp_json(struct NSPV_txidresp *utxos,int32_t numutxos) +UniValue NSPV_txidresp_json(struct NSPV_txidresp *txids,int32_t numutxos) { UniValue array(UniValue::VARR); int32_t i; for (i=0; i 0 ) - item.push_back(Pair("vout",(int64_t)utxos[i].vout)); - else item.push_back(Pair("vin",(int64_t)utxos[i].vout)); + item.push_back(Pair("height",(int64_t)txids[i].height)); + item.push_back(Pair("txid",txids[i].txid.GetHex())); + item.push_back(Pair("value",(double)txids[i].satoshis/COIN)); + if (txids[i].satoshis > 0) + item.push_back(Pair("vout", (int64_t)txids[i].index)); + else + item.push_back(Pair("vin", (int64_t)txids[i].index)); array.push_back(item); } return(array); @@ -443,7 +456,7 @@ UniValue NSPV_txidsresp_json(struct NSPV_txidsresp *ptr) result.push_back(Pair("isCC",ptr->CCflag)); result.push_back(Pair("height",(int64_t)ptr->nodeheight)); result.push_back(Pair("numtxids",(int64_t)ptr->numtxids)); - result.push_back(Pair("filter",(int64_t)ptr->filter)); + result.push_back(Pair("maxrecords",(int64_t)ptr->maxrecords)); result.push_back(Pair("lastpeer",NSPV_lastpeer)); return(result); } @@ -470,8 +483,7 @@ UniValue NSPV_ntzsresp_json(struct NSPV_ntzsresp *ptr) { UniValue result(UniValue::VOBJ); result.push_back(Pair("result","success")); - result.push_back(Pair("prev",NSPV_ntz_json(&ptr->prevntz))); - result.push_back(Pair("next",NSPV_ntz_json(&ptr->nextntz))); + result.push_back(Pair("ntz",NSPV_ntz_json(&ptr->ntz))); result.push_back(Pair("lastpeer",NSPV_lastpeer)); return(result); } @@ -480,16 +492,12 @@ UniValue NSPV_ntzsproof_json(struct NSPV_ntzsproofresp *ptr) { UniValue result(UniValue::VOBJ); result.push_back(Pair("result","success")); - result.push_back(Pair("prevht",(int64_t)ptr->common.prevht)); result.push_back(Pair("nextht",(int64_t)ptr->common.nextht)); - result.push_back(Pair("prevtxid",ptr->prevtxid.GetHex())); - result.push_back(Pair("prevtxidht",(int64_t)ptr->prevtxidht)); - result.push_back(Pair("prevtxlen",(int64_t)ptr->prevtxlen)); result.push_back(Pair("nexttxid",ptr->nexttxid.GetHex())); result.push_back(Pair("nexttxidht",(int64_t)ptr->nexttxidht)); result.push_back(Pair("nexttxlen",(int64_t)ptr->nexttxlen)); result.push_back(Pair("numhdrs",(int64_t)ptr->common.numhdrs)); - result.push_back(Pair("headers",NSPV_headers_json(ptr->common.hdrs,ptr->common.numhdrs,ptr->common.prevht))); + result.push_back(Pair("headers",NSPV_headers_json(ptr->common.hdrs, ptr->common.numhdrs, ptr->common.nextht))); result.push_back(Pair("lastpeer",NSPV_lastpeer)); //fprintf(stderr,"ntzs_proof %s %d, %s %d\n",ptr->prevtxid.GetHex().c_str(),ptr->common.prevht,ptr->nexttxid.GetHex().c_str(),ptr->common.nextht); return(result); @@ -809,19 +817,18 @@ UniValue NSPV_notarizations(int32_t reqheight) return(NSPV_ntzsresp_json(&N)); } -UniValue NSPV_txidhdrsproof(uint256 prevtxid,uint256 nexttxid) +UniValue NSPV_txidhdrsproof(uint256 nexttxid) { uint8_t msg[512]; int32_t i,iter,len = 0; struct NSPV_ntzsproofresp P,*ptr; - if ( (ptr= NSPV_ntzsproof_find(prevtxid,nexttxid)) != 0 ) + if ( (ptr= NSPV_ntzsproof_find(nexttxid)) != 0 ) { - fprintf(stderr,"FROM CACHE NSPV_txidhdrsproof %s %s\n",ptr->prevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str()); + fprintf(stderr,"FROM CACHE NSPV_txidhdrsproof %s\n", ptr->nexttxid.GetHex().c_str()); NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult); NSPV_ntzsproofresp_copy(&NSPV_ntzsproofresult,ptr); return(NSPV_ntzsproof_json(ptr)); } NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult); msg[len++] = NSPV_NTZSPROOF; - len += iguana_rwbignum(1,&msg[len],sizeof(prevtxid),(uint8_t *)&prevtxid); len += iguana_rwbignum(1,&msg[len],sizeof(nexttxid),(uint8_t *)&nexttxid); for (iter=0; iter<3; iter++) if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) @@ -829,7 +836,7 @@ UniValue NSPV_txidhdrsproof(uint256 prevtxid,uint256 nexttxid) for (i=0; icommon.nextht-ptr->common.prevht+1) != ptr->common.numhdrs ) + int16_t momdepthprev, momdepthnext; + const int32_t VALIDATESIGS = 1; + + /*if (ptr->common.depth != ptr->common.numhdrs) { fprintf(stderr,"next.%d prev.%d -> %d vs %d\n",ptr->common.nextht,ptr->common.prevht,ptr->common.nextht-ptr->common.prevht+1,ptr->common.numhdrs); return(-2); - } - else if ( NSPV_txextract(tx,ptr->nextntz,ptr->nexttxlen) < 0 ) + } else */ + if ( NSPV_txextract(tx,ptr->nextntz,ptr->nexttxlen) < 0 ) return(-3); else if ( tx.GetHash() != ptr->nexttxid ) return(-4); - else if ( NSPV_notarizationextract(1,&height,&blockhash,&desttxid,tx) < 0 ) + else if ( NSPV_notarizationextract(VALIDATESIGS, &height, &blockhash, &desttxid, &momdepthprev, tx) < 0 ) return(-5); else if ( height != ptr->common.nextht ) return(-6); @@ -44,23 +52,28 @@ int32_t NSPV_validatehdrs(struct NSPV_ntzsproofresp *ptr) if ( blockhash != ptr->common.hdrs[i].hashPrevBlock ) return(-i-13); } - sleep(1); // need this to get past the once per second rate limiter per message + /*sleep(1); // need this to get past the once per second rate limiter per message if ( NSPV_txextract(tx,ptr->prevntz,ptr->prevtxlen) < 0 ) return(-8); else if ( tx.GetHash() != ptr->prevtxid ) return(-9); - else if ( NSPV_notarizationextract(1,&height,&blockhash,&desttxid,tx) < 0 ) + else if ( NSPV_notarizationextract(VALIDATESIGS, &height, &blockhash, &desttxid, &momdepthnext, tx) < 0 ) return(-10); else if ( height != ptr->common.prevht ) return(-11); - else if ( NSPV_hdrhash(&ptr->common.hdrs[0]) != blockhash ) + else */ + if ( NSPV_hdrhash(&ptr->common.hdrs[0]) != blockhash ) return(-12); return(0); } int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int32_t height,CTransaction &tx,uint256 &hashblock,int32_t &txheight,int32_t ¤theight,int64_t extradata,uint32_t tiptime,int64_t &rewardsum) { - struct NSPV_txproof *ptr; int32_t i,offset,retval; int64_t rewards = 0; uint32_t nLockTime; std::vector proof; + struct NSPV_txproof* ptr; + int32_t i, offset, retval; + int64_t rewards = 0; + uint32_t nLockTime; + std::vector proof; retval = skipvalidation != 0 ? 0 : -1; //fprintf(stderr,"NSPV_gettx %s/v%d ht.%d\n",txid.GetHex().c_str(),vout,height); @@ -101,38 +114,48 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int proof.resize(ptr->txprooflen); memcpy(&proof[0],ptr->txproof,ptr->txprooflen); } - NSPV_notarizations(height); // gets the prev and next notarizations - if ( NSPV_inforesult.notarization.height >= height && (NSPV_ntzsresult.prevntz.height == 0 || NSPV_ntzsresult.prevntz.height >= NSPV_ntzsresult.nextntz.height) ) - { + + NSPV_ntz *ntzp; + if (height > NSPV_inforesult.ntz.ntzheight - NSPV_inforesult.ntz.depth && height <= NSPV_inforesult.ntz.ntzheight) + ntzp = &NSPV_inforesult.ntz; + else if (height > NSPV_ntzsresult.ntz.ntzheight - NSPV_ntzsresult.ntz.depth && height <= NSPV_ntzsresult.ntz.ntzheight) + ntzp = &NSPV_ntzsresult.ntz; + else { fprintf(stderr,"issue manual bracket\n"); - NSPV_notarizations(height-1); - NSPV_notarizations(height+1); - NSPV_notarizations(height); // gets the prev and next notarizations + NSPV_notarizations(height); // gets the notarization for tx height + ntzp = &NSPV_ntzsresult.ntz; + //NSPV_notarizations(height-1); + //NSPV_notarizations(height+1); + //NSPV_notarizations(height); // gets the prev and next notarizations } - if ( NSPV_ntzsresult.prevntz.height != 0 && NSPV_ntzsresult.prevntz.height <= NSPV_ntzsresult.nextntz.height ) + if (ntzp->txidheight != 0 && height > ntzp->ntzheight-ntzp->depth && height <= ntzp->ntzheight) { - fprintf(stderr,">>>>> gettx ht.%d prev.%d next.%d\n",height,NSPV_ntzsresult.prevntz.height, NSPV_ntzsresult.nextntz.height); - offset = (height - NSPV_ntzsresult.prevntz.height); - if ( offset >= 0 && height <= NSPV_ntzsresult.nextntz.height ) + fprintf(stderr,">>>>> gettx ht.%d next ntzht.%d\n", height, ntzp->ntzheight); + offset = height - (ntzp->ntzheight - ntzp->depth) + 1; // pos in headers + + //fprintf(stderr,"call NSPV_txidhdrsproof %s %s\n",NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.txid.GetHex().c_str()); + NSPV_txidhdrsproof(ntzp->txid); + //usleep(10000); + if ((retval = NSPV_validatehdrs(&NSPV_ntzsproofresult)) == 0) { - //fprintf(stderr,"call NSPV_txidhdrsproof %s %s\n",NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.txid.GetHex().c_str()); - NSPV_txidhdrsproof(NSPV_ntzsresult.prevntz.txid,NSPV_ntzsresult.nextntz.txid); - usleep(10000); - if ( (retval= NSPV_validatehdrs(&NSPV_ntzsproofresult)) == 0 ) + std::vector txids; uint256 proofroot; + proofroot = BitcoinGetProofMerkleRoot(proof,txids); + if ( proofroot != NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot || txids[0] != txid ) { - std::vector txids; uint256 proofroot; - proofroot = BitcoinGetProofMerkleRoot(proof,txids); - if ( proofroot != NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot || txids[0] != txid ) - { - fprintf(stderr,"txid.%s vs txids[0] %s\n",txid.GetHex().c_str(),txids[0].GetHex().c_str()); - fprintf(stderr,"prooflen.%d proofroot.%s vs %s\n",(int32_t)proof.size(),proofroot.GetHex().c_str(),NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot.GetHex().c_str()); - retval = -2003; - } else retval = 0; - } - } else retval = -2005; - } else retval = -2004; + fprintf(stderr,"txid.%s vs txids[0] %s\n",txid.GetHex().c_str(),txids[0].GetHex().c_str()); + fprintf(stderr,"prooflen.%d proofroot.%s vs %s\n",(int32_t)proof.size(),proofroot.GetHex().c_str(),NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot.GetHex().c_str()); + retval = -2003; + } + else + retval = 0; + } + else + retval = -2005; + } + else + retval = -2004; } - return(retval); + return retval; } int32_t NSPV_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *belowp,struct NSPV_utxoresp utxos[],int32_t numunspents,int64_t value) @@ -389,6 +412,7 @@ UniValue NSPV_spend(char *srcaddr,char *destaddr,int64_t satoshis) // what its a mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID; mtx.nVersion = SAPLING_TX_VERSION; if ( ASSETCHAINS_SYMBOL[0] == 0 ) { + LOCK(cs_main); if ( !komodo_hardfork_active((uint32_t)chainActive.LastTip()->nTime) ) mtx.nLockTime = (uint32_t)time(NULL) - 777; else @@ -493,42 +517,47 @@ void NSPV_utxos2CCunspents(struct NSPV_utxosresp *ptr,std::vector > &txids) +void NSPV_resp2indexOutputs(struct NSPV_txidsresp* ptr, std::vector>& indexOutputs) { - CAddressIndexKey key; int64_t value; int32_t i,type; uint160 hashBytes; std::string addrstr(ptr->coinaddr); - if ( ptr->txids != NULL && ptr->numtxids > 0 ) - { + if (!ptr->coinaddr) + return; + + CAddressIndexKey key; + int64_t value; + int32_t i, type; + uint160 hashBytes; + std::string addrstr(ptr->coinaddr); + + if (ptr->txids != NULL && ptr->numtxids > 0) { CBitcoinAddress address(addrstr); - if ( address.GetIndexKey(hashBytes, type, ptr->CCflag) == 0 ) - { - fprintf(stderr,"couldnt get indexkey\n"); + if (address.GetIndexKey(hashBytes, type, ptr->CCflag) == 0) { + fprintf(stderr, "couldnt get indexkey\n"); return; } - for (i = 0; i < ptr->numtxids; i ++) - { + for (i = 0; i < ptr->numtxids; i++) { key.type = type; key.hashBytes = hashBytes; key.txhash = ptr->txids[i].txid; - key.index = ptr->txids[i].vout; + key.index = ptr->txids[i].index; key.blockHeight = ptr->txids[i].height; value = ptr->txids[i].satoshis; - txids.push_back(std::make_pair(key, value)); + indexOutputs.push_back(std::make_pair(key, value)); } } } -void NSPV_CCunspents(std::vector > &outputs,char *coinaddr,bool ccflag) +void NSPV_CCunspents(std::vector > &outputs, char *coinaddr, bool ccflag) { int32_t filter = 0; NSPV_addressutxos(coinaddr,ccflag,0,filter); NSPV_utxos2CCunspents(&NSPV_utxosresult,outputs); } -void NSPV_CCtxids(std::vector > &txids,char *coinaddr,bool ccflag) +void NSPV_CCindexOutputs(std::vector>& indexOutputs, char* coinaddr, bool ccflag) { int32_t filter = 0; - NSPV_addresstxids(coinaddr,ccflag,0,filter); - NSPV_txids2CCtxids(&NSPV_txidsresult,txids); + NSPV_addresstxids(coinaddr, ccflag, 0, filter); + NSPV_resp2indexOutputs(&NSPV_txidsresult, indexOutputs); } void NSPV_CCtxids(std::vector &txids,char *coinaddr,bool ccflag, uint8_t evalcode,uint256 filtertxid, uint8_t func) diff --git a/src/komodo_utils.h b/src/komodo_utils.h index e3a9e58dbd7..5eef416bbd7 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -12,9 +12,14 @@ * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ -#include "komodo_defs.h" #include "key_io.h" +#include "net.h" +#include "util.h" +#include "komodo_defs.h" #include "cc/CCinclude.h" +#include "importcoin.h" +#include "cc/CCupgrades.h" + #include #ifdef _WIN32 @@ -815,7 +820,7 @@ int32_t komodo_is_issuer() int32_t bitweight(uint64_t x) { - int i,wt = 0; + int i, wt = 0; for (i=0; i<64; i++) if ( (1LL << i) & x ) wt++; @@ -1669,10 +1674,6 @@ uint64_t komodo_ac_block_subsidy(int nHeight) return(subsidy); } -extern int64_t MAX_MONEY; -void komodo_cbopretupdate(int32_t forceflag); -void SplitStr(const std::string& strVal, std::vector &outVals); - int8_t equihash_params_possible(uint64_t n, uint64_t k) { /* To add more of these you also need to edit: @@ -1772,10 +1773,10 @@ void komodo_args(char *argv0) ASSETCHAINS_PUBLIC = GetArg("-ac_public",0); ASSETCHAINS_PRIVATE = GetArg("-ac_private",0); KOMODO_SNAPSHOT_INTERVAL = GetArg("-ac_snapshot",0); - Split(GetArg("-ac_nk",""), sizeof(ASSETCHAINS_NK)/sizeof(*ASSETCHAINS_NK), ASSETCHAINS_NK, 0); + SplitIntoU64List(GetArg("-ac_nk",""), sizeof(ASSETCHAINS_NK)/sizeof(*ASSETCHAINS_NK), ASSETCHAINS_NK, 0); // -ac_ccactivateht=evalcode,height,evalcode,height,evalcode,height.... - Split(GetArg("-ac_ccactivateht",""), sizeof(ccEnablesHeight)/sizeof(*ccEnablesHeight), ccEnablesHeight, 0); + SplitIntoU64List(GetArg("-ac_ccactivateht",""), sizeof(ccEnablesHeight)/sizeof(*ccEnablesHeight), ccEnablesHeight, 0); // fill map with all eval codes and activation height of 0. for ( int i = 0; i < 256; i++ ) mapHeightEvalActivate[i] = 0; @@ -1849,11 +1850,11 @@ void komodo_args(char *argv0) ASSETCHAINS_TIMEUNLOCKFROM = ASSETCHAINS_TIMEUNLOCKTO = 0; } - Split(GetArg("-ac_end",""), sizeof(ASSETCHAINS_ENDSUBSIDY)/sizeof(*ASSETCHAINS_ENDSUBSIDY), ASSETCHAINS_ENDSUBSIDY, 0); - Split(GetArg("-ac_reward",""), sizeof(ASSETCHAINS_REWARD)/sizeof(*ASSETCHAINS_REWARD), ASSETCHAINS_REWARD, 0); - Split(GetArg("-ac_halving",""), sizeof(ASSETCHAINS_HALVING)/sizeof(*ASSETCHAINS_HALVING), ASSETCHAINS_HALVING, 0); - Split(GetArg("-ac_decay",""), sizeof(ASSETCHAINS_DECAY)/sizeof(*ASSETCHAINS_DECAY), ASSETCHAINS_DECAY, 0); - Split(GetArg("-ac_notarypay",""), sizeof(ASSETCHAINS_NOTARY_PAY)/sizeof(*ASSETCHAINS_NOTARY_PAY), ASSETCHAINS_NOTARY_PAY, 0); + SplitIntoU64List(GetArg("-ac_end",""), sizeof(ASSETCHAINS_ENDSUBSIDY)/sizeof(*ASSETCHAINS_ENDSUBSIDY), ASSETCHAINS_ENDSUBSIDY, 0); + SplitIntoU64List(GetArg("-ac_reward",""), sizeof(ASSETCHAINS_REWARD)/sizeof(*ASSETCHAINS_REWARD), ASSETCHAINS_REWARD, 0); + SplitIntoU64List(GetArg("-ac_halving",""), sizeof(ASSETCHAINS_HALVING)/sizeof(*ASSETCHAINS_HALVING), ASSETCHAINS_HALVING, 0); + SplitIntoU64List(GetArg("-ac_decay",""), sizeof(ASSETCHAINS_DECAY)/sizeof(*ASSETCHAINS_DECAY), ASSETCHAINS_DECAY, 0); + SplitIntoU64List(GetArg("-ac_notarypay",""), sizeof(ASSETCHAINS_NOTARY_PAY)/sizeof(*ASSETCHAINS_NOTARY_PAY), ASSETCHAINS_NOTARY_PAY, 0); for ( int i = 0; i < ASSETCHAINS_MAX_ERAS; i++ ) { @@ -1923,7 +1924,7 @@ void komodo_args(char *argv0) { uint8_t prevCCi = 0; ASSETCHAINS_CCLIB = GetArg("-ac_cclib",""); - Split(GetArg("-ac_ccenable",""), sizeof(ccenables)/sizeof(*ccenables), ccenables, 0); + SplitIntoU64List(GetArg("-ac_ccenable",""), sizeof(ccenables)/sizeof(*ccenables), ccenables, 0); for (i=nonz=0; i<0x100; i++) { if ( ccenables[i] != prevCCi && ccenables[i] != 0 ) @@ -1989,7 +1990,7 @@ void komodo_args(char *argv0) } else if ( ASSETCHAINS_SELFIMPORT == "PEGSCC") { - Split(GetArg("-ac_pegsccparams",""), sizeof(ASSETCHAINS_PEGSCCPARAMS)/sizeof(*ASSETCHAINS_PEGSCCPARAMS), ASSETCHAINS_PEGSCCPARAMS, 0); + SplitIntoU64List(GetArg("-ac_pegsccparams",""), sizeof(ASSETCHAINS_PEGSCCPARAMS)/sizeof(*ASSETCHAINS_PEGSCCPARAMS), ASSETCHAINS_PEGSCCPARAMS, 0); if (ASSETCHAINS_ENDSUBSIDY[0]!=1 || ASSETCHAINS_COMMISSION!=0) { fprintf(stderr,"when using import for pegsCC these must be set: -ac_end=1 -ac_perc=0\n"); @@ -2437,6 +2438,10 @@ fprintf(stderr,"extralen.%d before disable bits\n",extralen); KOMODO_DPOWCONFS = GetArg("-dpowconfs",dpowconfs); if ( ASSETCHAINS_SYMBOL[0] == 0 || strcmp(ASSETCHAINS_SYMBOL,"SUPERNET") == 0 || strcmp(ASSETCHAINS_SYMBOL,"DEX") == 0 || strcmp(ASSETCHAINS_SYMBOL,"COQUI") == 0 || strcmp(ASSETCHAINS_SYMBOL,"PIRATE") == 0 || strcmp(ASSETCHAINS_SYMBOL,"KMDICE") == 0 ) KOMODO_EXTRASATOSHI = 1; + + if (ASSETCHAINS_CC != 0) + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); + } void komodo_nameset(char *symbol,char *dest,char *source) @@ -2494,4 +2499,164 @@ void komodo_prefetch(FILE *fp) /* bool komodo_Is2021JuneHFActive() { return GetLatestTimestamp(komodo_currentheight()) > JUNE2021_NNELECTION_HARDFORK; -} */ \ No newline at end of file +} */ + +int32_t KOMODO_LONGESTCHAIN; +int32_t komodo_longestchain() +{ + static int32_t depth; + int32_t ht,n=0,num=0,maxheight=0,height = 0; + if ( depth < 0 ) + depth = 0; + if ( depth == 0 ) + { + + /** + * Seems here we need to try to lock cs_main, to avoid wrong order of lock (cs_main, cs_vNodes), + * implementation of getting max(nStartingHeight, nSyncHeight, nCommonHeight) from CNodeStateStats + * and loop here is similar to getpeerinfo RPC and there we have LOCK(cs_main). If we'll not able + * to acquire lock on cs_main komodo_longestchain() will return previous saved value of + * KOMODO_LONGESTCHAIN, anyway, on next call it will be updated, when lock will success. + */ + + TRY_LOCK(cs_main, lockMain); // Acquire cs_main + if (!lockMain) { + return(KOMODO_LONGESTCHAIN); + } + + depth++; + vector vstats; + { + //LOCK(cs_main); + CopyNodeStats(vstats); + } + BOOST_FOREACH(const CNodeStats& stats, vstats) + { + //fprintf(stderr,"komodo_longestchain iter.%d\n",n); + CNodeStateStats statestats; + bool fStateStats = GetNodeStateStats(stats.nodeid,statestats); + if ( statestats.nSyncHeight < 0 ) + continue; + ht = 0; + if ( stats.nStartingHeight > ht ) + ht = stats.nStartingHeight; + if ( statestats.nSyncHeight > ht ) + ht = statestats.nSyncHeight; + if ( statestats.nCommonHeight > ht ) + ht = statestats.nCommonHeight; + if ( maxheight == 0 || ht > maxheight*1.01 ) + maxheight = ht, num = 1; + else if ( ht > maxheight*0.99 ) + num++; + if ( ht > height ) + height = ht; + } + depth--; + if ( num > (n >> 1) ) + { + if ( 0 && height != KOMODO_LONGESTCHAIN ) + fprintf(stderr,"set %s KOMODO_LONGESTCHAIN <- %d\n",ASSETCHAINS_SYMBOL,height); + KOMODO_LONGESTCHAIN = height; + return(height); + } + KOMODO_LONGESTCHAIN = 0; + } + return(KOMODO_LONGESTCHAIN); +} + +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; +} + +int32_t komodo_get_current_height() +{ + if ( KOMODO_NSPV_SUPERLITE ) + { + return (NSPV_inforesult.height); + } + else return chainActive.LastTip()->GetHeight(); +} + +bool komodo_txnotarizedconfirmed(uint256 txid, int32_t minconfirms) +{ + char str[65]; + int32_t confirms,minimumconfirms,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 ) + { + fprintf(stderr,"komodo_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str()); + return(0); + } + else if (txheight<=0) + { + fprintf(stderr,"komodo_txnotarizedconfirmed no txheight.%d for txid %s\n",txheight,txid.ToString().c_str()); + return(0); + } + else if (txheight>currentheight) + { + fprintf(stderr,"komodo_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,currentheight); + return(0); + } + confirms=1 + currentheight - txheight; + } + else + { + if ( myGetTransaction(txid,tx,hashBlock) == 0 ) + { + fprintf(stderr,"komodo_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str()); + return(0); + } + else if ( hashBlock == zeroid ) + { + fprintf(stderr,"komodo_txnotarizedconfirmed no hashBlock for txid %s\n",txid.ToString().c_str()); + return(0); + } + else if ( (pindex= komodo_blockindex(hashBlock)) == 0 || (txheight= pindex->GetHeight()) <= 0 ) + { + fprintf(stderr,"komodo_txnotarizedconfirmed no txheight.%d %p for txid %s\n",txheight,pindex,txid.ToString().c_str()); + return(0); + } + else if ( (pindex= chainActive.LastTip()) == 0 || pindex->GetHeight() < txheight ) + { + fprintf(stderr,"komodo_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,(int32_t)pindex->GetHeight()); + return(0); + } + 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) + return (true); + return (false); +} + +// creates a nLockTime value for a new tx, with Dec 2019 hardfork check +uint32_t komodo_next_tx_locktime() +{ + AssertLockHeld(cs_main); + if (!komodo_hardfork_active((uint32_t)chainActive.LastTip()->nTime)) + return (uint32_t)chainActive.LastTip()->nTime + 1; // set to a time close to now + else + return (uint32_t)chainActive.Tip()->GetMedianTimePast(); +} \ No newline at end of file diff --git a/src/komodo_version.h b/src/komodo_version.h new file mode 100644 index 00000000000..c84b4645362 --- /dev/null +++ b/src/komodo_version.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * Copyright © 2022 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 KOMODO_VERSION_H +#define KOMODO_VERSION_H + +#include +#include "clientversion.h" +#include "config/bitcoin-config.h" + +// version = major * 1000000 + minor * 10000 + rev * 100 + build +//const int KOMODO_VERSION = 60000; +//const int TOKEL_VERSION = 30100; + +const std::string KOMODO_CLIENT_NAME = std::string("komodod:") + FormatVersion(KOMODO_VERSION); +const std::string TOKEL_CLIENT_NAME = std::string("tokeld:") + FormatVersion(TOKEL_VERSION); + + +#endif // #ifndef KOMODO_VERSION_H diff --git a/src/komodo_websockets.cpp b/src/komodo_websockets.cpp new file mode 100644 index 00000000000..a6bd09a34f3 --- /dev/null +++ b/src/komodo_websockets.cpp @@ -0,0 +1,1750 @@ +// Copyright (c) 2020 The SuperNet developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// websockets support for komodod +// based on websocketspp library +// introduces new commands getwsaddr/wsaddr to get list of websockets listener +// to build ws listener table a object of CAddrMan is used, wsaddrman +// generally the same protocol is used like for the original p2p addrman: +// + + +//#include "httpserver.h" +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "timedata.h" +#include "main.h" +#include "consensus/validation.h" +#include "util.h" +#include "net.h" +#include "addrman.h" +#include "sync.h" +#include "utilstrencodings.h" +#include "univalue.h" +#include "rpc/server.h" + +#include "cc/CCupgrades.h" + + +//#include + +#ifndef ENABLE_WEBSOCKETS +#error "ENABLE_WEBSOCKETS not defined" +#endif + +#include "komodo_websockets.h" + +namespace ws +{ + +static CAddrMan wsaddrman; + +static bool bWebSocketsStarted = false; +static bool fWebSocketsInWarmup = true; +static bool fWebSocketsStopping = false; + +static CCriticalSection cs_wsWarmup; + +static CSemaphore *semWsOutbound = NULL; +static boost::condition_variable wsMessageHandlerCondition; + + +static std::set setservAddNodeWsAddresses; +static CCriticalSection cs_setservAddNodeWsAddresses; + +static std::vector vAddedWsNodes; +static CCriticalSection cs_vAddedWsNodes; + +typedef websocketpp::lib::shared_ptr ws_thread_ptr; +static std::vector vWsThreads; + +static boost::thread_group wsThreadGroup; + +unsigned short GetWebSocketListenPort() +{ + return (unsigned short)(GetArg("-wsport", 8192)); +} + +int GetHeight() +{ + LOCK(cs_main); + CBlockIndex* pindex; + if ((pindex = chainActive.LastTip()) != nullptr) + return pindex->GetHeight(); + else + return 0; +} +int64_t GetTipTime() +{ + LOCK(cs_main); + CBlockIndex* pindex; + if ((pindex = chainActive.LastTip()) != nullptr) + return pindex->nTime; + else + return 0; +} + +CAddress GetLocalWebSocketAddress(const CNetAddr *paddrPeer) +{ + CAddress ret(CService("0.0.0.0", GetWebSocketListenPort()), 0); + // TODO: decide if we need websocket listeners bound to specific local address (not 0.0.0.0) + CService addr; + if (GetLocal(addr, paddrPeer)) + { + addr.SetPort(GetWebSocketListenPort()); + ret = CAddress(addr); + } + ret.nServices = NODE_NETWORK | NODE_WEBSOCKETS; + if (GetBoolArg("-nspv_msg", DEFAULT_NSPV_PROCESSING)) + ret.nServices |= NODE_NSPV; + ret.nTime = GetTime(); + return ret; +} + +// advertizes websockets listen address +void AdvertizeLocalWebSockets(CNode *pnode) +{ + if (/*fListen && <-- TODO: add this flag*/ pnode->fSuccessfullyConnected) + { + CAddress addrLocal = GetLocalWebSocketAddress(&pnode->addr); + // If discovery is enabled, sometimes give our peer the address it + // tells us that it sees us as in case it has a better idea of our + // address than we do. + if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() || + GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8:2) == 0)) + { + addrLocal.SetIP(pnode->addrLocal); + } + if (addrLocal.IsRoutable()) + { + LogPrintf("AdvertizeLocalWebSockets: advertizing local websocket address %s for peer %d\n", addrLocal.ToString(), pnode->id); + pnode->PushAddress(addrLocal); + } + } +} + +class CWsNode; + +// base wrapper class both for server and outbound endpoints +class CWsEndpointWrapper { +public: + CWsEndpointWrapper() {} + virtual void send(websocketpp::connection_hdl hdl, void const * payload, size_t len, + websocketpp::frame::opcode::value op, websocketpp::lib::error_code & ec) = 0; + + virtual void close(websocketpp::connection_hdl hdl, websocketpp::close::status::value) = 0; + virtual void sendWsData(CWsNode *pNode) = 0; +}; + +typedef std::shared_ptr ws_endpoint_ptr; +static ws_endpoint_ptr spWebSocketServer; + +class CWsNode : public CNode { +public: + CWsNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false) + : CNode(hSocketIn, addrIn, addrNameIn, fInboundIn) + { + closeErrorOnSend = 0; + closeErrorOnReceive = 0; + nLastRebroadcast = 0; + } + + websocketpp::connection_hdl m_hdl; + ws_endpoint_ptr m_spWsEndpoint; + websocketpp::close::status::value closeErrorOnSend; + websocketpp::close::status::value closeErrorOnReceive; + int64_t nLastRebroadcast; // for rebroacasting local address + + void PushWsVersion() + { + int nBestHeight = GetNodeSignals().GetHeight().get_value_or(0); + + int64_t nTime = (fInbound ? GetTime() : GetTime()); + CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); + CAddress addrMe = GetLocalWebSocketAddress(&addr); + GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + if (fLogIPs) + LogPrint("websockets", "send websocket version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id); + else + LogPrint("websockets", "send websocket version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); + PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, + nLocalHostNonce, strSubVersion, nBestHeight, true); + } + + ~CWsNode() { + wsaddrman.Connected(addr); + } +}; + +typedef std::shared_ptr CWsNodePtr; +static std::vector vWsNodes; // websocket own node list +static CCriticalSection cs_vWsNodes; + +static std::set vWsNodesDisconnected; // websocket disconnected nodes +static CCriticalSection cs_vWsNodesDisconnected; + +class CWebSocketOutbound; +static std::vector vOutboundEndpoints; // wait until enpoint opens +static CCriticalSection cs_vOutboundEndpoints; + +static CWsNodePtr FindWsNode(const CNetAddr& ip) +{ + LOCK(cs_vWsNodes); + for(auto const & pnode : vWsNodes) + if ((CNetAddr)pnode->addr == ip) + return (pnode); + return NULL; +} + +static CWsNodePtr FindWsNode(const CSubNet& subNet) +{ + LOCK(cs_vWsNodes); + for(auto const & pnode : vWsNodes) + if (subNet.Match((CNetAddr)pnode->addr)) + return (pnode); + return NULL; +} + +static CWsNodePtr FindWsNode(const std::string& addrName) +{ + LOCK(cs_vWsNodes); + for(auto const & pnode : vWsNodes) + if (pnode->addrName == addrName) + return (pnode); + return NULL; +} + +static CWsNodePtr FindWsNode(const CService& addr) +{ + LOCK(cs_vWsNodes); + for(auto const & pnode : vWsNodes) + if ((CService)pnode->addr == addr) + return (pnode); + return NULL; +} + + +static void RemoveWsNode(CWsNodePtr pNode) +{ + AssertLockHeld(cs_vWsNodes); + vWsNodes.erase(std::remove(vWsNodes.begin(), vWsNodes.end(), pNode), vWsNodes.end()); + LOCK(cs_vWsNodesDisconnected); + vWsNodesDisconnected.insert(pNode); +} + +// returns true if message was recognized +bool ProcessWsMessage(CNode* pfrom, std::string strCommand, CDataStream& vRecv, int64_t nTimeReceived) +{ + if (strCommand == "version") + { + // Each connection can only send one version message + if (pfrom->nVersion != 0) + { + pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, std::string("Duplicate version message")); + Misbehaving(pfrom->GetId(), 1); + return true; + } + + int64_t nTime; + CAddress addrMe; + CAddress addrFrom; + uint64_t nNonce = 1; + int nVersion; // use temporary for version, don't set version number until validated as connected + int minVersion = MIN_PEER_PROTO_VERSION; + + //if ( is_STAKED(ASSETCHAINS_SYMBOL) != 0 ) + // minVersion = STAKEDMIN_PEER_PROTO_VERSION; + vRecv >> nVersion >> pfrom->nServices >> nTime >> addrMe; + if (nVersion == 10300) + nVersion = 300; + if (nVersion < minVersion) + { + // disconnect from peers older than this proto version + LogPrintf("wspeer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", minVersion)); + pfrom->fDisconnect = true; + return true; + } + + // zcash upgrades are not relevant to websockets: + // Reject incoming connections from nodes that don't know about the current epoch + /*const Consensus::Params& params = Params().GetConsensus(); + auto currentEpoch = CurrentEpoch(GetHeight(), params); + if (nVersion < params.vUpgrades[currentEpoch].nProtocolVersion) + { + LogPrintf("wspeer=%d using obsolete version %i; disconnecting\n", pfrom->id, nVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", + params.vUpgrades[currentEpoch].nProtocolVersion)); + pfrom->fDisconnect = true; + return false; + }*/ + + const int64_t nTipTime = GetTipTime(); + const int nHeight = GetHeight(); + // check min cc version + if (nVersion < GetCurrentUpgradeInfo(nTipTime, nHeight, CCUpgrades::GetUpgrades()).nProtocolVersion) + { + LogPrint("websockets", "wspeer=%d using obsolete version %i; disconnecting by ccupgrades\n", pfrom->id, nVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", + GetCurrentUpgradeInfo(nTipTime, nHeight, CCUpgrades::GetUpgrades()).nProtocolVersion)); + pfrom->fDisconnect = true; + return true; + } + + if (!vRecv.empty()) + vRecv >> addrFrom >> nNonce; + if (!vRecv.empty()) { + vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH); + pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); + } + if (!vRecv.empty()) + vRecv >> pfrom->nStartingHeight; + + // not relevant to websockets: + /* if (!vRecv.empty()) + vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message + else + pfrom->fRelayTxes = true;*/ + + // Disconnect if we connected to ourself + if (nNonce == nLocalHostNonce && nNonce > 1) + { + LogPrintf("websockets connected to self at %s, disconnecting\n", pfrom->addr.ToString()); + pfrom->fDisconnect = true; + return true; + } + + pfrom->nVersion = nVersion; + + pfrom->addrLocal = addrMe; + if (pfrom->fInbound && addrMe.IsRoutable()) + { + // SeenLocal(addrMe); // TODO: better not to influence the main p2p net + } + + // Be shy and don't send version until we hear + if (pfrom->fInbound) + static_cast(pfrom)->PushWsVersion(); + + pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); + + // not relevant to websockets + // Potentially mark this peer as a preferred download peer. + //UpdatePreferredDownload(pfrom, State(pfrom->GetId())); + + // Change version + pfrom->PushMessage("verack"); + pfrom->ssSend.SetVersion(std::min(pfrom->nVersion, PROTOCOL_VERSION)); + + if (!pfrom->fInbound) + { + + // Advertise our ws address + // but only if this node connected to remote (outbound) nodes + // note that because of that a listening node does not advertise its local address on "version" cmd + // also the outbound node will get back its local white address only on the next addr message + // the outbound node will get the next "wsaddr" on "getwsaddr" response + // (note that the first "getaddr" for the first outbound will return none as no any advertised local addr yet on the listening node) + // plus "wsaddr" cmd is sent in SendMessages will also have a empty address list to send + // Note, that "addrMe" from a remote node from "version" is not added to AddrLocal + if (!IsInitialBlockDownload()) // TODO: isWebsocketStarted + { + // get best real local websocket listening address (known outside) and advertize it + CAddress addr = GetLocalWebSocketAddress(&pfrom->addr); + if (addr.IsRoutable()) + { + LogPrintf("%s: advertizing local websocket address %s for peer %d (from local)\n", __func__, addr.ToString(), pfrom->GetId()); + pfrom->PushAddress(addr); + } else if (IsPeerAddrLocalGood(pfrom)) { + addr.SetIP(pfrom->addrLocal); + LogPrintf("%s: advertizing local websocket address %s for peer %d (from node)\n", __func__, addr.ToString(), pfrom->GetId()); + pfrom->PushAddress(addr); + } + } + else + { + LogPrint("websockets", "note: websockets local address advertising skipped: IsInitialBlockDownload()=%d\n", IsInitialBlockDownload()); + } + + // Get recent websocket addresses + if (pfrom->fOneShot || pfrom->nVersion >= WSADDR_VERSION || wsaddrman.size() < 1000) + { + LogPrint("websockets", "pushing getwsaddr request for peer=%d\n", pfrom->GetId()); + pfrom->PushMessage("getwsaddr"); + } + wsaddrman.Good(pfrom->addr); + } + else + { + std::cerr << __func__ << " version from inbound pfrom->addr=" << pfrom->addr.ToStringIPPort() << " (CNetAddr)pfrom->addr=" << ((CNetAddr)pfrom->addr).ToString() << " (CNetAddr)addrFrom=" << ((CNetAddr)addrFrom).ToString() << std::endl; + if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom) + { + LogPrint("websockets", " storing in wsaddrman inbound pfrom->addr=%s\n", pfrom->addr.ToStringIPPort()); + wsaddrman.Add(addrFrom, addrFrom); + wsaddrman.Good(addrFrom); + } + // can't add here remote wsaddr for the node as we dont know the websocket port (as it is not contained in the version message and we have not extended it) + } + + LogPrint("websockets", "websockets version received: from addr=%s peer=%d\n", pfrom->addr.ToStringIPPort(), pfrom->id); + pfrom->fSuccessfullyConnected = true; + return true; + } + + // websocket nodes propagation + else if (strCommand == "wsaddr") + { + std::vector vAddr; + vRecv >> vAddr; + + LogPrint("websockets", "cmd \"wsaddr\" received, vWsaddr.size=%d from peer=%d\n", vAddr.size(), pfrom->id); + + // Don't want wsaddr from older versions + if (pfrom->nVersion < WSADDR_VERSION) { + LogPrintf("version too old for wsaddr %d peer %d", pfrom->nVersion, pfrom->GetId()); + return true; + } + + if (wsaddrman.size() > 1000) { + LogPrintf("websockets addrman full, don't accept wsaddr from peer %d", pfrom->GetId()); + return true; + } + + if (vAddr.size() > 1000) { + LogPrintf("websockets node misbehaving, message wsaddr size() = %u peer %d", vAddr.size(), pfrom->GetId()); + Misbehaving(pfrom->GetId(), 20); + return true; + } + + // Store the new addresses + std::vector vAddrOk; + int64_t nNow = GetTime(); + int64_t nSince = nNow - 10 * 60; + BOOST_FOREACH(CAddress& addr, vAddr) + { + boost::this_thread::interruption_point(); + + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) + addr.nTime = nNow - 5 * 24 * 60 * 60; + pfrom->AddAddressKnown(addr); + bool fReachable = IsReachable(addr); + if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) + { + // Relay to a limited number of other nodes + { + LOCK(cs_vWsNodes); + // Use deterministic randomness to send to the same ws nodes for 24 hours + // at a time so the wsaddrKnowns of the chosen nodes prevent repeats + static uint256 hashSalt; + if (hashSalt.IsNull()) + hashSalt = GetRandHash(); + uint64_t hashAddr = addr.GetHash(); + uint256 hashRand = ArithToUint256(UintToArith256(hashSalt) ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60))); + hashRand = Hash(BEGIN(hashRand), END(hashRand)); + std::multimap mapMix; + for(auto const & pnode : vWsNodes) + { + if (pnode->nVersion >= WSADDR_VERSION && (pnode->nServices & NODE_WEBSOCKETS) != 0) // should be relayed to any node supporting "wsaddr" + { + unsigned int nPointer; + memcpy(&nPointer, &pnode, sizeof(nPointer)); + uint256 hashKey = ArithToUint256(UintToArith256(hashRand) ^ nPointer); + hashKey = Hash(BEGIN(hashKey), END(hashKey)); + mapMix.insert(std::make_pair(hashKey, pnode)); + } + } + int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) + for (std::multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) { + LogPrint("websockets"," relaying ws address=%s to peer=%d\n", addr.ToStringIPPort(), ((*mi).second)->GetId()); + ((*mi).second)->PushAddress(addr); + } + } + } + // Do not store addresses outside our network + if (fReachable) { + vAddrOk.push_back(addr); + LogPrint("websockets", "cmd \"wsaddr\": adding to wsaddrman addr=%s from peer=%d\n", addr.ToString(), pfrom->id); + } + else + LogPrint("websockets", "cmd \"wsaddr\": not reachable addr=%s from peer=%d\n", addr.ToString(), pfrom->id); + } + wsaddrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60); + + if (vAddr.size() < 1000) + pfrom->fGetAddr = false; + return true; + } + + else if ((strCommand == "getwsaddr") && (pfrom->fInbound)) // allow to getwsaddr for clients and newly connected nodes to initialize their addrman + { + // rate limit for getwsaddr requests + if (GetTime() - pfrom->nLastWsAddrTime < 60) { + LogPrint("websockets", "Ignoring repeated \"getwsaddr\". peer=%d\n", pfrom->id); + return true; + } + pfrom->nLastWsAddrTime = GetTime(); + + pfrom->vAddrToSend.clear(); + std::vector vWsaddr = wsaddrman.GetAddrAtMost(); + LogPrint("websockets", "received getwsaddr cmd, found vAddr.size=%s from peer=%d\n", vWsaddr.size(), pfrom->GetId()); + BOOST_FOREACH(const CAddress &addr, vWsaddr) { + LogPrint("websockets", "in response to getwsaddr pushing wsaddr=%s\n", addr.ToString()); + pfrom->PushAddress(addr); + } + return true; + } + + return false; +} + +// create periodical messages +bool SendWsMessages(CWsNode *pto, bool fTrickle) +{ + + if (pto->nVersion == 0) + return true; + + // + // Message: ping on websockets + // + bool pingSend = false; + if (pto->fPingQueued) { + // RPC ping request by user + pingSend = true; + } + if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) { + // Ping automatically sent as a latency probe & keepalive. + pingSend = true; + } + if (pingSend) { + uint64_t nonce = 0; + while (nonce == 0) { + GetRandBytes((unsigned char*)&nonce, sizeof(nonce)); + } + pto->fPingQueued = false; + pto->nPingUsecStart = GetTimeMicros(); + if (pto->nVersion > BIP0031_VERSION) { + pto->nPingNonceSent = nonce; + pto->PushMessage("ping", nonce); + } else { + // Peer is too old to support ping command with nonce, pong will never arrive. + pto->nPingNonceSent = 0; + pto->PushMessage("ping"); + } + } + + // Address refresh broadcast + static int64_t nLastRebroadcast; + //MilliSleep(1000); + int64_t nCurrentTime = GetTime(); + if (nCurrentTime - nLastRebroadcast > 60) // check every 60 sec + { + // for the node clear known addresses each 24h and advertize local ws listener address + LOCK(cs_vWsNodes); + for(auto const pnode : vWsNodes) + { + if (nCurrentTime - pnode->nLastRebroadcast > 24 * 60 * 60) { // rebroadcast to each node each 24h + pnode->addrKnown.reset(); + AdvertizeLocalWebSockets(pnode.get()); + pnode->nLastRebroadcast = nCurrentTime; + } + } + nLastRebroadcast = nCurrentTime; + } + + if (fTrickle) + { + // if there are addresses to send for this node send them + std::vector vAddr; + vAddr.reserve(pto->vAddrToSend.size()); + for(const CAddress& addr : pto->vAddrToSend) + { + if (!pto->addrKnown.contains(addr.GetKey())) + { + pto->AddAddressKnown(addr); + vAddr.push_back(addr); + LogPrint("websockets", "sending message with ws address=%s to peer=%d\n", addr.ToStringIPPort(), pto->GetId()); + // receiver rejects addr messages larger than 1000 + if (vAddr.size() >= 1000) + { + pto->PushMessage("wsaddr", vAddr); + LogPrint("websockets", "sent %d websocket addresses to peer %d\n", vAddr.size(), pto->id); + vAddr.clear(); + } + } + } + pto->vAddrToSend.clear(); + if (!vAddr.empty()) { + pto->PushMessage("wsaddr", vAddr); + LogPrint("websockets", "sent %d websocket addresses to peer %d\n", vAddr.size(), pto->id); + } + } + + return true; +} + + +// requires LOCK(cs_vSend) +void WebSocketSendData(CWsEndpointWrapper *pEndPoint, websocketpp::connection_hdl hdl, CWsNode *pnode) +{ + std::deque::iterator it = pnode->vSendMsg.begin(); + pnode->closeErrorOnSend = 0; + + while (it != pnode->vSendMsg.end()) { + const CSerializeData &data = *it; + assert(data.size() > pnode->nSendOffset); + websocketpp::lib::error_code ec; + int nBytes = data.size() - pnode->nSendOffset; + pEndPoint->send(hdl, &data[pnode->nSendOffset], nBytes, websocketpp::frame::opcode::binary, ec); // should not throw ws exception as ec is passed + + if (!ec) { + pnode->nLastSend = GetTime(); // needed to prevent inactivity disconnect + pnode->nSendBytes += nBytes; + pnode->nSendOffset += nBytes; + pnode->RecordBytesSent(nBytes); + if (pnode->nSendOffset == data.size()) { + pnode->nSendOffset = 0; + pnode->nSendSize -= data.size(); + it++; + } else { + // could not send full message; stop sending more + break; + } + } else { // error + // int nErr = WSAGetLastError(); + // if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) // no similar errs in websocketpp... + { + LogPrint("websockets", "websocket send error %d %s\n", ec.value(), ec.category().name()); + pnode->closeErrorOnSend = websocketpp::close::status::try_again_later; + pnode->fDisconnect; + } + // couldn't send anything at all + break; + } + } + + if (it == pnode->vSendMsg.end()) { + assert(pnode->nSendOffset == 0); + assert(pnode->nSendSize == 0); + } + pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); +} + +void HandleWebSocketMessage(CWsEndpointWrapper *pEndPoint, CWsNode *pNode, websocketpp::connection_hdl hdl, wsserver::message_ptr msg) +{ + pNode->closeErrorOnReceive = 0; + + LOCK(pNode->cs_vRecvMsg); + if (pNode->ReceiveMsgBytes(msg->get_payload().c_str(), msg->get_payload().size())) { + pNode->nLastRecv = GetTime(); // needed to prevent inactivity disconnect + pNode->nRecvBytes += msg->get_payload().size(); + pNode->RecordBytesRecv(msg->get_payload().size()); + if (ProcessMessages(pNode)) { + LOCK(pNode->cs_vSend); + WebSocketSendData(pEndPoint, hdl, pNode); + } + } + else { + LogPrint("websockets", "error websocket message processing, disconnecting peer %d\n", pNode->id); + pNode->closeErrorOnReceive = websocketpp::close::status::unsupported_data; + pNode->fDisconnect = true; + } +} + + +class CWebSocketServer : public CWsEndpointWrapper { +public: + bool init() { + + LogPrintf("Starting websockets listener\n"); + + try { + + // Set logging settings + m_endpoint.set_error_channels(websocketpp::log::elevel::rerror); // & ~websocketpp::log::elevel::devel); + m_endpoint.set_access_channels(websocketpp::log::alevel::none); + + // Initialize Asio + m_endpoint.init_asio(); + m_endpoint.set_reuse_addr(true); // this would prevent komodod restart failure on the socket in use + // bcz the ws socket might be in CLOSE_WAIT state for a long time komodod has finished (especially if local app has not close the socket), + // however this is not recommended as allows for other apps to use this socket as shared (security issue) + // looks like still a websocketpp issue with socket shutdown + // Set the default message handler to the echo handler + m_endpoint.set_message_handler(std::bind( + &CWebSocketServer::on_message, this, + std::placeholders::_1, std::placeholders::_2 + )); + + // enable for tls: + // m_endpoint.set_tls_init_handler(bind(&CWebSocketServer::on_tls_init, this, MOZILLA_MODERN, _1)); // add tls init + m_endpoint.set_open_handler(bind(&CWebSocketServer::on_open, this, _1)); + m_endpoint.set_close_handler(bind(&CWebSocketServer::on_close, this, _1)); + m_endpoint.set_validate_handler(bind(&CWebSocketServer::on_validate, this, _1)); + m_endpoint.set_fail_handler(bind(&CWebSocketServer::on_fail, this, _1)); + + } + catch (websocketpp::exception const & e) { + LogPrintf("websockets init failed: %s (for possible a reason check openssl lib 1.1.1 is installed)\n", e.what()); + return false; + } + return true; + } + + bool run() + { + try { + // Listen on -wsport + m_endpoint.listen(GetWebSocketListenPort()); + + // Queues a connection accept operation + m_endpoint.start_accept(); + + // Start the Asio io_service run loop + //m_endpoint.run(); + for (int i = 0; i < 4; i ++) + vWsThreads.push_back(websocketpp::lib::make_shared(&wsserver::run, &m_endpoint)); + } + catch (websocketpp::exception const & e) { + LogPrintf("websockets listener run failed: %s \n", e.what()); + return false; + } + return true; + } + + void on_message(websocketpp::connection_hdl hdl, wsserver::message_ptr msg) { + boost::this_thread::interruption_point(); + // write a new message + //std::cerr << __func__ << " payload=" << HexStr(msg->get_payload()) << std::endl; + //m_endpoint.send(hdl, msg->get_payload(), msg->get_opcode()); + CAddress clientAddr = GetClientAddressFromHdl(hdl); + LOCK(cs_vWsNodes); + CWsNodePtr pNode = FindWsNode(clientAddr); + //CWsNode *pNode = m_connections[hdl]; + if (!pNode) { + return; + } + if (pNode->fDisconnect) { + return; + } + + //std::cerr << __func__ << " pnode found=" << clientAddr.ToStringIPPort() << " id=" << pNode->id << std::endl; + + HandleWebSocketMessage(this, pNode.get(), hdl, msg); + } + + void stop() + { + LogPrintf("Stopping websocket listener...\n"); + websocketpp::lib::error_code ec; + m_endpoint.stop_listening(ec); + + //LogPrintf("closing websocket connections...\n"); + std::vector vWsNodesCopy; + { + LOCK(cs_vWsNodes); + vWsNodesCopy = vWsNodes; + } + + for(const auto & pnode : vWsNodesCopy) + { + if (pnode->fInbound) + { + //wsserver::connection_ptr conn_ptr = m_endpoint.get_con_from_hdl(conn.first); + try { + pnode->m_spWsEndpoint->close(pnode->m_hdl, websocketpp::close::status::going_away); + } catch (websocketpp::exception const & e) { // might be already close from remote site or on a error + LogPrint("websockets", "%s stop listener websocketpp::exception: %s (this could be okay)\n", __func__, e.what()); + } + } + } + + LogPrintf("Waiting for websocket threads to stop... \n(Note that a possible following 'asio async_shutdown error' message is an expected behaviour)\n"); + // expalined here https://github.com/zaphoyd/websocketpp/issues/556 + // see also a possible workaround https://github.com/zaphoyd/websocketpp/issues/545 + // and http://docs.websocketpp.org/faq.html + /*for (size_t i = 0; i < vWsThreads.size(); i++) { + vWsThreads[i]->interrupt(); + }*/ + for (size_t i = 0; i < vWsThreads.size(); i++) { + vWsThreads[i]->join(); + } + //m_endpoint.close(); + //LOCK(cs_vWsNodes); + //for (auto const &pNode : vWsNodes) + // RemoveWsNode(pNode); + std::cerr << __func__ << " waiting for listener endpoint stopped state..." << std::endl; + while(!m_endpoint.stopped()) + MilliSleep(250); + + LogPrintf("Websocket listener stopped\n"); + } + + virtual void send(websocketpp::connection_hdl hdl, void const * payload, size_t len, websocketpp::frame::opcode::value op, websocketpp::lib::error_code & ec) { + m_endpoint.send(hdl, payload, len, op, ec); + } + +private: + bool on_validate(websocketpp::connection_hdl hdl) + { + return !fWebSocketsInWarmup; + } + + /* test password + std::string get_password() { + return "test"; + } */ + + /* enable for tls + context_ptr on_tls_init(tls_mode mode, websocketpp::connection_hdl hdl) { + namespace asio = websocketpp::lib::asio; + + std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl; + std::cout << "using TLS mode: " << (mode == MOZILLA_MODERN ? "Mozilla Modern" : "Mozilla Intermediate") << std::endl; + + context_ptr ctx = websocketpp::lib::make_shared(asio::ssl::context::sslv23); + + try { + if (mode == MOZILLA_MODERN) { + // Modern disables TLSv1 + ctx->set_options(asio::ssl::context::default_workarounds | + asio::ssl::context::no_sslv2 | + asio::ssl::context::no_sslv3 | + asio::ssl::context::no_tlsv1 | + asio::ssl::context::single_dh_use); + } else { + ctx->set_options(asio::ssl::context::default_workarounds | + asio::ssl::context::no_sslv2 | + asio::ssl::context::no_sslv3 | + asio::ssl::context::single_dh_use); + } + ctx->set_password_callback(bind(&CWebSocketServer::get_password, this)); + + // self signed test certificate generated with: + // openssl req -newkey rsa:2048 -nodes -subj '/CN=komodo.test' -keyout key.pem -x509 -days 365 -out certificate.crt + // cat certificate.crt key.pem > server.pem + ctx->use_certificate_chain_file("server.pem"); + ctx->use_private_key_file("server.pem", asio::ssl::context::pem); + + // Example method of generating this file: + // `openssl dhparam -out dh.pem 2048` + // Mozilla Intermediate suggests 1024 as the minimum size to use + // Mozilla Modern suggests 2048 as the minimum size to use. + ctx->use_tmp_dh_file("dh.pem"); + + std::string ciphers; + + if (mode == MOZILLA_MODERN) { + ciphers = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"; + } else { + ciphers = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; + } + + if (SSL_CTX_set_cipher_list(ctx->native_handle() , ciphers.c_str()) != 1) { + std::cout << "Error setting cipher list" << std::endl; + } + } catch (std::exception& e) { + std::cout << "Exception: " << e.what() << std::endl; + } + return ctx; + } */ + + void on_fail(websocketpp::connection_hdl hdl) { + wsserver::connection_ptr con = m_endpoint.get_con_from_hdl(hdl); + + //std::cout << "Websocket server error for remote peer " << GetClientAddressFromHdl(hdl).ToStringIPPort() << ": " << con->get_ec() << " - " << con->get_ec().message() << std::endl; + /*std::cout << con->get_state() << std::endl; + std::cout << con->get_local_close_code() << std::endl; + std::cout << con->get_local_close_reason() << std::endl; + std::cout << con->get_remote_close_code() << std::endl; + std::cout << con->get_remote_close_reason() << std::endl; + std::cout << con->get_ec() << " - " << con->get_ec().message() << std::endl;*/ + CAddress clientAddr = GetClientAddressFromHdl(hdl); + LOCK(cs_vWsNodes); + CWsNodePtr pNode = FindWsNode(clientAddr); + if (!pNode) { + return; + } + + LogPrint("websockets", "error on inbound connection from ws peer %d\n", pNode->GetId()); + pNode->fDisconnect = true; + // RemoveWsNode(pNode); // TODO: make remove the same way like in CWebSocketOutbound, maybe better if nodes are removed in the common loop in net.cpp + // NOTE: do not modify the pNode might be deleted in RemoveWsNode + } + + void on_open(websocketpp::connection_hdl hdl) + { + CAddress addr = GetClientAddressFromHdl(hdl); + + CWsNodePtr pNode( new CWsNode(INVALID_SOCKET, addr, "", true) ); // not used hSocket for websockets + pNode->m_spWsEndpoint = spWebSocketServer; + pNode->m_hdl = hdl; + { + LOCK(cs_vWsNodes); + vWsNodes.push_back(pNode); + } + } + + void on_close(websocketpp::connection_hdl hdl) + { + CAddress clientAddr = GetClientAddressFromHdl(hdl); + LOCK(cs_vWsNodes); + CWsNodePtr pNode = FindWsNode(clientAddr); + if (!pNode) { + return; + } + + LogPrint("websockets", "closed inbound connection from ws peer %d\n", pNode->GetId()); + + pNode->fDisconnect = true; + RemoveWsNode(pNode); + } + + virtual void close(websocketpp::connection_hdl hdl, websocketpp::close::status::value status) + { + LogPrint("websockets", "closing connection %s called for wsserver\n", GetClientAddressFromHdl(hdl).ToStringIPPort().c_str()); + m_endpoint.close(hdl, status, ""); + } + + virtual void sendWsData(CWsNode *pNode) + { + LOCK(pNode->cs_vSend); + WebSocketSendData(this, pNode->m_hdl, pNode); + } + + CAddress GetClientAddressFromHdl(websocketpp::connection_hdl hdl) + { + wsserver::connection_ptr conn_ptr = m_endpoint.get_con_from_hdl(hdl); + std::string sAddr = conn_ptr->get_remote_endpoint(); + CService svc(sAddr); + return CAddress(svc); + } +private: + wsserver m_endpoint; +}; + +// object to try peer websocket nodes +class CWebSocketOutbound : public CWsEndpointWrapper { +public: + + CWebSocketOutbound () { + m_endpoint.set_error_channels(websocketpp::log::elevel::rerror); + m_endpoint.set_access_channels(websocketpp::log::alevel::none); + + // Initialize ASIO + m_endpoint.init_asio(); + + // Register our handlers + m_endpoint.set_socket_init_handler(bind(&CWebSocketOutbound::on_socket_init,this,::_1)); + + // enable for tls version: + //m_endpoint.set_http_init_handler(bind(&CWebSocketOutbound::on_socket_init,this,::_1)); + //m_endpoint.set_tls_init_handler(bind(&CWebSocketOutbound::on_socket_init,this,::_1)); + + m_endpoint.set_message_handler(bind(&CWebSocketOutbound::on_message,this,::_1,::_2)); + m_endpoint.set_open_handler(bind(&CWebSocketOutbound::on_open,this,::_1)); + m_endpoint.set_close_handler(bind(&CWebSocketOutbound::on_close,this,::_1)); + m_endpoint.set_fail_handler(bind(&CWebSocketOutbound::on_fail,this,::_1)); + + m_bFailed = false; + } + + ~CWebSocketOutbound() { + // do not delete m_pNode, it is deleted in NetCleanUp + } + + bool start(CAddress addrConnect, std::string uri) + { + if (fWebSocketsInWarmup) + return false; + + websocketpp::lib::error_code ec; + + m_addrConnect = addrConnect; + if (!uri.empty()) + m_uri = uri; + else + m_uri = "ws://" + addrConnect.ToStringIPPort(); // use wss for tls version + + wsclient::connection_ptr con = m_endpoint.get_connection(m_uri, ec); + + if (ec) { + m_endpoint.get_alog().write(websocketpp::log::alevel::app, ec.message()); + return false; + } + + + m_endpoint.connect(con); + return true; + } + + void run() + { + LOCK(cs); + m_thread = websocketpp::lib::make_shared(&wsclient::run, &m_endpoint); + } + + void on_socket_init(websocketpp::connection_hdl hdl) { + } + + void on_fail(websocketpp::connection_hdl hdl) { + //wsclient::connection_ptr conn_ptr = m_endpoint.get_con_from_hdl(hdl); + m_bFailed = true; + if (m_pNode) { + LOCK(cs_vWsNodes); + LogPrint("websockets", "error on outbound connection to websocket peer %d\n", m_pNode->GetId()); + + m_pNode->fDisconnect = true; // mark for disconnection + RemoveWsNode(m_pNode); + } + else { + LogPrint("websockets", "could not connect to remote websocket peer %s\n", m_uri.c_str()); + } + } + + void on_open(websocketpp::connection_hdl hdl) { + + // Add node + { + LOCK2(cs, cs_vOutboundEndpoints); + m_pNode.reset( new CWsNode(INVALID_SOCKET, m_addrConnect, (!m_uri.empty() ? m_uri : ""), false) ); + m_pNode->m_spWsEndpoint = *(std::find_if(vOutboundEndpoints.begin(), vOutboundEndpoints.end(), [&](const ws_endpoint_ptr& sp){ return sp.get() == static_cast(this); })); + } + + m_pNode->m_hdl = hdl; + m_pNode->nTimeConnected = GetTime(); + m_pNode->fNetworkNode = true; + + { + LOCK(cs_vWsNodes); + vWsNodes.push_back(m_pNode); + } + + m_pNode->PushWsVersion(); + { + LOCK(m_pNode->cs_vSend); + WebSocketSendData(this, hdl, m_pNode.get()); + } + } + void on_message(websocketpp::connection_hdl hdl, wsclient::message_ptr msg) { + HandleWebSocketMessage(this, m_pNode.get(), hdl, msg); + } + void on_close(websocketpp::connection_hdl) { + if ((bool)m_pNode) { + LOCK(cs_vWsNodes); + LogPrint("websockets", "closed outbound connection to ws peer %d\n", m_pNode->GetId()); + + m_pNode->fDisconnect = true; + RemoveWsNode(m_pNode); + } + } + + virtual void close(websocketpp::connection_hdl hdl, websocketpp::close::status::value status) + { + if (m_endpoint.get_con_from_hdl(hdl) != nullptr) { + LogPrint("websockets", "closing outbound connection to %s called\n", m_pNode->addr.ToString()); + m_endpoint.close(hdl, status, std::string()); + } + } + + virtual void send(websocketpp::connection_hdl hdl, void const * payload, size_t len, websocketpp::frame::opcode::value op, websocketpp::lib::error_code & ec) { + m_endpoint.send(hdl, payload, len, op, ec); + } + + virtual void sendWsData(CWsNode*) + { + if (m_pNode) { + LOCK(m_pNode->cs_vSend); + WebSocketSendData(this, m_pNode->m_hdl, m_pNode.get()); + } + } + +private: + wsclient m_endpoint; + std::string m_uri; + CAddress m_addrConnect; + +public: + CWsNodePtr m_pNode; + ws_thread_ptr m_thread; + bool m_bFailed; + CCriticalSection cs; +}; + +CWebSocketOutbound* ConnectWsNode(CAddress addrConnect, const char *pszDest) +{ + if (pszDest == NULL) { + if (IsLocal(addrConnect)) + return NULL; + + // Look for an existing connection + CWsNodePtr pnode = FindWsNode((CService)addrConnect); + if ((bool)pnode) + { + return NULL; + } + } + + /// debug print + LogPrint("websockets", "trying websockets connection %s lastseen=%.1fhrs\n", + pszDest ? pszDest : addrConnect.ToString(), + pszDest ? 0.0 : (double)(GetTime() - addrConnect.nTime)/3600.0); + + // Connect + bool proxyConnectionFailed = false; + + CWebSocketOutbound *pwsOutbound = new CWebSocketOutbound(); + bool bStarted; + try { + bStarted = pwsOutbound->start(addrConnect, pszDest ? pszDest : ""); + } + catch (websocketpp::exception const & e) { + LogPrint("websockets", "could not start connection to websockets peer %s %s\n", addrConnect.ToStringIPPort().c_str(), pszDest ? pszDest : ""); + delete pwsOutbound; + return NULL; + } + if (bStarted) { + wsaddrman.Attempt(addrConnect); + } else if (!proxyConnectionFailed) { + // If connecting to the node failed, and failure is not caused by a problem connecting to + // the proxy, mark this as an attempt. + wsaddrman.Attempt(addrConnect); + } + + return pwsOutbound; +} + +// if successful, this moves the passed grant to the constructed node +bool OpenWebSocketNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound /*, const char *pszDest, bool fOneShot*/) +{ + // + // Initiate outbound network connection + // + boost::this_thread::interruption_point(); + //if (!pszDest) { + if (IsLocal(addrConnect) || + FindWsNode((CNetAddr)addrConnect) || CWsNode::IsBanned(addrConnect) || + FindWsNode(addrConnect.ToStringIPPort())) + return false; + //} else if (FindWsNode(std::string(pszDest))) + // return false; + + CWebSocketOutbound * pOutbound = ConnectWsNode(addrConnect, NULL /*pszDest*/); // Note: pOutbound ptr is stored in pNode (which itself is stored in vWsNodes) and handled from there + boost::this_thread::interruption_point(); + + if (!pOutbound) + return false; + + // Start the ASIO io_service run loop + pOutbound->run(); + + { + LOCK(cs_vOutboundEndpoints); + vOutboundEndpoints.push_back(ws_endpoint_ptr(pOutbound)); + } + //if (fOneShot) + // pnode->fOneShot = true; // one shot not implemented + + return true; +} + +// select a node and create messages to send +void ThreadWebSocketMessageHandler() +{ + boost::mutex condition_mutex; + boost::unique_lock lock(condition_mutex); + + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); + while (true) + { + boost::this_thread::interruption_point(); + + std::vector vWsNodesCopy; + { + LOCK(cs_vWsNodes); + vWsNodesCopy = vWsNodes; + } + + CWsNodePtr pnodeTrickle = NULL; + if (!vWsNodesCopy.empty()) + pnodeTrickle = vWsNodesCopy[GetRand(vWsNodesCopy.size())]; + + bool fSleep = true; + + for(auto const & pnode : vWsNodesCopy) + { + if (pnode->fDisconnect) + continue; + + // Send messages + { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) { + bool fTrickle = pnode == pnodeTrickle || pnode->fWhitelisted; + SendWsMessages(pnode.get(), fTrickle); + if (fTrickle) { + WebSocketSendData(pnode->m_spWsEndpoint.get(), pnode->m_hdl, pnode.get()); + if (pnode->closeErrorOnSend || pnode->closeErrorOnReceive) { + try { + pnode->m_spWsEndpoint->close(pnode->m_hdl, (pnode->closeErrorOnSend ? pnode->closeErrorOnSend : pnode->closeErrorOnReceive)); + } catch (websocketpp::exception const & e) { // might be already closed from remote site or on a error + LogPrint("websockets", "%s close websocketpp::exception: %s (could be normal)\n", __func__, e.what()); + } + } + } + } + } + + boost::this_thread::interruption_point(); + } + + if (fSleep) + wsMessageHandlerCondition.timed_wait(lock, boost::posix_time::microsec_clock::universal_time() + boost::posix_time::milliseconds(100)); + } +} + +// join for disconnected outbound threads to stop +static void ThreadWebSocketWaitForDisconnectedThreads() +{ + boost::mutex condition_mutex; + boost::unique_lock lock(condition_mutex); + + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); + while (true) + { + boost::this_thread::interruption_point(); + + // remove failed outbound endpoints + std::vector vOutboundEndpointsCopy; + { + LOCK(cs_vOutboundEndpoints); + vOutboundEndpointsCopy = vOutboundEndpoints; + } + for (auto &spOutbound : vOutboundEndpointsCopy) + { + CWebSocketOutbound *pOutbound = static_cast(spOutbound.get()); + bool bJoin = false; + bool bRemove = false; + { + LOCK(pOutbound->cs); + bJoin = (bool)pOutbound->m_bFailed && !pOutbound->m_pNode && (bool)pOutbound->m_thread || (bool)pOutbound->m_thread && fWebSocketsStopping; // join outbound threads that never connected + // stop tracking either failed outbounds with no node created or + bRemove = (bool)pOutbound->m_pNode || pOutbound->m_bFailed || fWebSocketsStopping; + } + if (bRemove) + { + { + LOCK(pOutbound->cs); + if (bJoin && (bool)pOutbound->m_thread && pOutbound->m_thread->joinable()) { + pOutbound->m_thread->join(); + } + } + { + LOCK(cs_vOutboundEndpoints); + auto it = std::remove(vOutboundEndpoints.begin(), vOutboundEndpoints.end(), spOutbound); + vOutboundEndpoints.erase(it, vOutboundEndpoints.end()); + } + } + } + + std::vector vWsNodesCopy; + { + LOCK(cs_vWsNodes); + vWsNodesCopy = vWsNodes; + } + for(auto const & pnode : vWsNodesCopy) + { + // + // Inactivity checking + // + int64_t nTime = GetTime(); + if (nTime - pnode->nTimeConnected > 60) + { + if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) + { + LogPrint("websockets", "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id); + pnode->fDisconnect = true; + } + /* in websockets we may not send messages for some time, we only do getwsaddr sometimes: + else if (nTime - pnode->nLastSend > WEBSOCKETS_TIMEOUT_INTERVAL) + { + LogPrintf("websocket sending timeout: %is peer=%d\n", nTime - pnode->nLastSend, pnode->GetId()); + pnode->fDisconnect = true; + } */ + /* in websockets our getwsaddr requests may be not answered: + else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? WEBSOCKETS_TIMEOUT_INTERVAL : 90*60)) + { + LogPrintf("websocket receive timeout: %is peer=%d\n", nTime - pnode->nLastRecv, pnode->GetId()); + pnode->fDisconnect = true; + } */ + else if (pnode->nPingNonceSent && pnode->nPingUsecStart + WEBSOCKETS_TIMEOUT_INTERVAL * 1000000 < GetTimeMicros()) + { + LogPrintf("websocket ping timeout: %fs peer=%d\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart), pnode->GetId()); + pnode->fDisconnect = true; + } + + if (pnode->fDisconnect) + { + try { + pnode->m_spWsEndpoint->close(pnode->m_hdl, websocketpp::close::status::no_status); + } catch (websocketpp::exception const & e) { // might be already close from remote site or on a error + LogPrint("websockets", "%s close websocketpp::exception: %s (this may be normal if peer already remotely closed)\n", __func__, e.what()); + LOCK(cs_vWsNodes); + RemoveWsNode(pnode); + } + } + } + } + + // make a copy in order to not to block while waiting for thread join + std::set vWsNodesDisconnectedCopy; + { + LOCK(cs_vWsNodesDisconnected); + vWsNodesDisconnectedCopy = vWsNodesDisconnected; + vWsNodesDisconnected.clear(); + + } + + // join and delete + for(auto itnode = vWsNodesDisconnectedCopy.begin() ; itnode != vWsNodesDisconnectedCopy.end(); ) + { + if (!(*itnode)->fInbound) + { + ws_thread_ptr outboundThreadPtr = static_cast((*itnode)->m_spWsEndpoint.get())->m_thread; + if ((bool)outboundThreadPtr && outboundThreadPtr->joinable()) + outboundThreadPtr->join(); // wait for outbound thread to stop + + } + auto itnext = vWsNodesDisconnectedCopy.erase(itnode); + itnode = itnext; + + } + + MilliSleep(1000); + } +} + +static int GetOutboundNodes() +{ + LOCK(cs_vWsNodes); + int nOutbounds = 0; + for (const auto &pNode : vWsNodes) + if (!pNode->fInbound) + nOutbounds ++; + return nOutbounds; +} + +void ThreadOpenWebSocketConnections() +{ + // Connect to specific addresses + /*if (mapArgs.count("-wsconnect") && mapMultiArgs["-wsconnect"].size() > 0) + { + for (int64_t nLoop = 0;; nLoop++) + { + ProcessOneShot(); + BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-wsconnect"]) + { + CAddress addr; + OpenNetworkConnection(addr, NULL, strAddr.c_str()); + for (int i = 0; i < 10 && i < nLoop; i++) + { + MilliSleep(500); + } + } + MilliSleep(500); + } + }*/ + + // Initiate network connections + int64_t nStart = GetTime(); + while (true) + { + //ProcessOneShot(); + + MilliSleep(2000); + + CSemaphoreGrant grant; + boost::this_thread::interruption_point(); + + if (fWebSocketsStopping) + continue; + + // Add seed nodes if DNS seeds are all down (an infrastructure attack?). + // if (addrman.size() == 0 && (GetTime() - nStart > 60)) { + /*if (GetTime() - nStart > 60) { + static bool done = false; + if (!done) { + // skip DNS seeds for staked chains. + if ( is_STAKED(ASSETCHAINS_SYMBOL) == 0 && KOMODO_DEX_P2P == 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")); + } + done = true; + } + }*/ + + + // + // Choose an address to connect to based on most recently seen + // + CAddress addrConnect; + + // Only connect out to one peer per network group (/16 for IPv4). + // Do this here so we don't have to critsect vWsNodes inside mapAddresses critsect. + int nOutbound = 0; + std::set > setConnected; + { + LOCK(cs_vWsNodes); + for (auto const & pnode : vWsNodes) { + if (!pnode->fInbound) { + setConnected.insert(pnode->addr.GetGroup(wsaddrman.m_asmap)); + nOutbound++; + } + } + } + + if (GetOutboundNodes() >= 8) + continue; + + int64_t nANow = GetTime(); + + int nTries = 0; + while (true) + { + CAddrInfo addr = wsaddrman.Select(); + + // if we selected an invalid address, restart + if (!addr.IsValid() || setConnected.count(addr.GetGroup(wsaddrman.m_asmap)) || IsLocal(addr)) + break; + +#ifdef ENABLE_WEBSOCKETS + // do not connect to client nodes (looks like this should be added for non-websocket version too): + if ((addr.nServices & NODE_NETWORK) == 0) + continue; +#endif + + // If we didn't find an appropriate destination after trying 100 addresses fetched from addrman, + // stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates + // already-connected network ranges, ...) before trying new addrman addresses. + nTries++; + if (nTries > 100) + break; + + if (IsLimited(addr)) + continue; + + // only consider very recently tried nodes after 30 failed attempts + if (nANow - addr.nLastTry < 600 && nTries < 30) + continue; + + // no default port for websockets. TODO: set up a websockets default port + // do not allow non-default ports, unless after 50 invalid addresses selected already + //if (addr.GetPort() != Params().GetDefaultPort() && nTries < 50) + // continue; + + addrConnect = addr; + break; + } + + if (addrConnect.IsValid()) { + OpenWebSocketNetworkConnection(addrConnect, &grant); + } + } +} + +static void ThreadOpenAddedWebSocketConnections() +{ + { + LOCK(cs_vAddedWsNodes); + vAddedWsNodes = mapMultiArgs["-addwsnode"]; + } + + /*if (HaveNameProxy()) { + // don't use proxy for websocket listeners + }*/ + + for (unsigned int i = 0; true; i++) + { + boost::this_thread::interruption_point(); + + std::list lAddresses(0); + { + LOCK(cs_vAddedNodes); + for(const std::string& strAddNode : vAddedWsNodes) + lAddresses.push_back(strAddNode); + } + + std::list > lservAddressesToAdd(0); + for(const std::string& strAddNode : lAddresses) { + std::vector vservNode(0); + if(Lookup(strAddNode.c_str(), vservNode, GetWebSocketListenPort(), fNameLookup, 0)) + { + lservAddressesToAdd.push_back(vservNode); + { + LOCK(cs_setservAddNodeWsAddresses); + BOOST_FOREACH(const CService& serv, vservNode) + setservAddNodeWsAddresses.insert(serv); // seems not used + } + } + } + // Attempt to connect to each IP for each addnode entry until at least one is successful per addnode entry + // (keeping in mind that addnode entries can have many IPs if fNameLookup) + { + LOCK(cs_vWsNodes); + for(auto const & pnode : vWsNodes) + { + for (std::list >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++) + { + BOOST_FOREACH(const CService& addrNode, *(it)) + if (pnode->addr == addrNode) + { + it = lservAddressesToAdd.erase(it); + if ( it != lservAddressesToAdd.begin() ) + it--; + break; + } + if (it == lservAddressesToAdd.end()) + break; + } + } + } + if (!fWebSocketsStopping) + { + for(std::vector& vserv : lservAddressesToAdd) + { + if (GetOutboundNodes() >= 8) + break; + CSemaphoreGrant grant; + OpenWebSocketNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); + MilliSleep(500); + } + } + MilliSleep(120000); // Retry every 2 minutes + } +} + + +// start stop + +bool StartWebSockets(boost::thread_group& threadGroup) +{ + spWebSocketServer.reset(new CWebSocketServer); + + if (!static_cast(spWebSocketServer.get())->init()) + return false; + if (!static_cast(spWebSocketServer.get())->run()) + return false; + + //if (semWsOutbound == NULL) + // semWsOutbound = new CSemaphore(8); // allow 8 peer connections + + // Initiate outbound connections + wsThreadGroup.create_thread(boost::bind(&TraceThread, "wsopencon", &ThreadOpenWebSocketConnections)); + + // Initiate outbound 'addwsnode' connections + wsThreadGroup.create_thread(boost::bind(&TraceThread, "wsopenaddedcon", &ThreadOpenAddedWebSocketConnections)); + + // periodical send messages thread + wsThreadGroup.create_thread(boost::bind(&TraceThread, "wsmsghand", &ThreadWebSocketMessageHandler)); + + // wait for disconnected oupud threads + wsThreadGroup.create_thread(boost::bind(&TraceThread, "wsdiscon", &ThreadWebSocketWaitForDisconnectedThreads)); + + bWebSocketsStarted = true; + return true; +} + +void SetWebSocketsWarmupFinished() +{ + LOCK(cs_wsWarmup); + assert(fWebSocketsInWarmup); + fWebSocketsInWarmup = false; +} + + +void StopWebSockets() +{ + if (!bWebSocketsStarted) + return; + + if (!spWebSocketServer) + return; + + fWebSocketsStopping = true; + static_cast(spWebSocketServer.get())->stop(); + + LogPrintf("Closing outbound websocket connections...\n"); + { + std::vector vWsNodesCopy; + { + LOCK(cs_vWsNodes); + vWsNodesCopy = vWsNodes; + } + for(const auto &pnode : vWsNodesCopy) + { + if (!pnode->fInbound) { // inbounds are close when the ws listener stops + try { + pnode->m_spWsEndpoint->close(pnode->m_hdl, websocketpp::close::status::going_away); + } catch (websocketpp::exception const & e) { // might be already close from remote site or on a error + LogPrint("websockets", "%s close websocketpp::exception: %s (this may be normal)", __func__, e.what()); + } + } + } + } + + LogPrintf("Waiting for websockets outbound threads to stop...\n"); + // joined in "wsdiscon" thread: + while(vWsNodesDisconnected.size() > 0 || GetOutboundNodes() > 0 || vOutboundEndpoints.size() > 0) { + //std::cerr << __func__ << " vWsNodesDisconnected.size()=" << vWsNodesDisconnected.size() << " GetOutboundNodes()=" << GetOutboundNodes() << " vOutboundEndpoints.size()=" << vOutboundEndpoints.size() << std::endl; + MilliSleep(500); + } + + LogPrintf("Waiting for websockets work threads to stop...\n"); + + wsThreadGroup.interrupt_all(); + wsThreadGroup.join_all(); + + LogPrintf("All websockets threads stopped\n"); + +} + +// debug rpc impl +UniValue GetWsPeers() +{ + UniValue result(UniValue::VARR); + std::vector vstats; + + { + LOCK(cs_vWsNodes); + vstats.reserve(vWsNodes.size()); + for(const auto & pnode : vWsNodes) { + CNodeStats stats; + pnode->copyStats(stats, wsaddrman.m_asmap); + vstats.push_back(stats); + } + } + + for (auto const &stats : vstats) { + UniValue peer(UniValue::VOBJ); + + peer.push_back(Pair("id", stats.nodeid)); + peer.push_back(Pair("addr", stats.addrName)); + if (!(stats.addrLocal.empty())) + peer.push_back(Pair("addrlocal", stats.addrLocal)); + peer.push_back(Pair("services", strprintf("%016x", stats.nServices))); + peer.push_back(Pair("version", stats.nVersion)); + // Use the sanitized form of subver here, to avoid tricksy remote peers from + // corrupting or modifying the JSON output by putting special characters in + // their ver message. + peer.push_back(Pair("subver", stats.cleanSubVer)); + peer.push_back(Pair("inbound", stats.fInbound)); + + result.push_back(peer); + } + return result; +} + +// temp rpc to dump addrman table. TODO: remove this code +/* +UniValue printaddrman(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || params.size() != 0) + { + std::string msg = "printaddrman\n" + "\nReturns an array of addresses\n"; + throw std::runtime_error(msg); + } + std::vector addrInfos = addrman.GetAddrInfoAll(); + UniValue result (UniValue::VOBJ); + UniValue array (UniValue::VARR); + int64_t nNow = GetTime(); + for(auto const & a : addrInfos) { + UniValue e(UniValue::VOBJ); + + e.pushKV("address", a.ToString()); + std::ostringstream strhex; + strhex << std::hex << a.nServices; + e.pushKV("services", strhex.str()); + e.pushKV("source", addrman.GetSource(a).ToString()); + e.pushKV("IsTerrible", a.IsTerrible()); + e.pushKV("SinceLastTry", nNow - a.nLastTry); + e.pushKV("SinceLastSeen", nNow - a.nTime); + array.push_back(e); + } + result.pushKV("addresses", array); + + return result; +} +*/ + +/* +// temp rpc to dump addrman table. TODO: remove this code +UniValue printwsaddrman(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || params.size() != 0) + { + std::string msg = "printwsaddrman\n" + "\nReturns an array of addresses\n"; + throw std::runtime_error(msg); + } + + std::vector addrInfos = wsaddrman.GetAddrInfoAll(); + UniValue result (UniValue::VOBJ); + UniValue array (UniValue::VARR); + int64_t nNow = GetTime(); + for(auto const & a : addrInfos) { + UniValue e(UniValue::VOBJ); + + e.pushKV("wsaddress", a.ToString()); + std::ostringstream strhex; + strhex << std::hex << a.nServices; + e.pushKV("services", strhex.str()); + e.pushKV("source", wsaddrman.GetSource(a).ToString()); + e.pushKV("IsTerrible", a.IsTerrible()); + e.pushKV("SinceLastTry", nNow - a.nLastTry); + e.pushKV("SinceLastSeen", nNow - a.nTime); + array.push_back(e); + } + result.pushKV("wsaddresses", array); + + return result; +} +*/ + +UniValue getwspeers(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + UniValue result = GetWsPeers(); + return result; +} + +}; // namespace ws + +static const CRPCCommand commands[] = +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- +// { "hidden", "printaddrman", &ws::printaddrman, true }, +// { "hidden", "printwsaddrman", &ws::printwsaddrman, true }, + { "hidden", "getwspeers", &ws::getwspeers, true }, +}; + +void RegisterWebSocketsRPCCommands(CRPCTable &tableRPC) +{ + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); +} \ No newline at end of file diff --git a/src/komodo_websockets.h b/src/komodo_websockets.h new file mode 100644 index 00000000000..30945ac5ed7 --- /dev/null +++ b/src/komodo_websockets.h @@ -0,0 +1,86 @@ +/****************************************************************************** + * Copyright © 2014-2022 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 __KOMODO_WEBSOCKETS_H__ +#define __KOMODO_WEBSOCKETS_H__ + +#include + +// The ASIO_STANDALONE define is necessary to use the standalone version of Asio. +// Remove if you are using Boost Asio. +// #define ASIO_STANDALONE +//#include +#include +#include +#include +#include +#include +#include +//#include + +//using websocketpp::lib::bind; + +namespace ws +{ + +static const int WSADDR_VERSION = 170008; +#define WEBSOCKETS_TIMEOUT_INTERVAL 120 + + +struct wsserver_mt_config : public websocketpp::config::asio { // no tls +// struct wsserver_mt_config : public websocketpp::config::asio_tls { // tls + + // pull default settings from our core config + static bool const enable_multithreading = true; + + struct transport_config : public websocketpp::config::core::transport_config { + static bool const enable_multithreading = true; + }; + + /// permessage_compress extension + // struct permessage_deflate_config {}; + + // typedef websocketpp::extensions::permessage_deflate::enabled + // permessage_deflate_type; +}; + +typedef websocketpp::server wsserver; // no tls +// typedef websocketpp::server wsserver; // tls + +// transport::asio::tls_socket::endpoint + +typedef websocketpp::client wsclient; + + +// typedef websocketpp::lib::shared_ptr context_ptr; + +// See https://wiki.mozilla.org/Security/Server_Side_TLS for more details about +// the TLS modes. The code below demonstrates how to implement both the modern +/* enum tls_mode { + MOZILLA_INTERMEDIATE = 1, + MOZILLA_MODERN = 2 +}; */ + +bool StartWebSockets(boost::thread_group& threadGroup); +void SetWebSocketsWarmupFinished(); +void StopWebSockets(); + +bool ProcessWsMessage(CNode* pfrom, std::string strCommand, CDataStream& vRecv, int64_t nTimeReceived); + +}; // namespace ws + +int GetnScore(const CService& addr); // from net.cpp + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index cbadd81609d..443097bba4a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -66,6 +66,10 @@ #include #include +#ifdef ENABLE_WEBSOCKETS +#include "komodo_websockets.h" +#endif + using namespace std; #if defined(NDEBUG) @@ -80,14 +84,8 @@ using namespace std; #define TMPFILE_START 100000000 CCriticalSection cs_main; -extern uint8_t NOTARY_PUBKEY33[33]; -extern int32_t KOMODO_LOADINGBLOCKS,KOMODO_LONGESTCHAIN,KOMODO_INSYNC,KOMODO_CONNECTING,KOMODO_EXTRASATOSHI; int32_t KOMODO_NEWBLOCKS; -int32_t komodo_block2pubkey33(uint8_t *pubkey33,CBlock *block); -//void komodo_broadcast(CBlock *pblock,int32_t limit); -bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey); -void komodo_setactivation(int32_t height); -void komodo_pricesupdate(int32_t height,CBlock *pblock); + BlockMap mapBlockIndex; CChain chainActive; @@ -357,9 +355,18 @@ namespace { int GetHeight() { CBlockIndex *pindex; - if ( (pindex= chainActive.LastTip()) != 0 ) + if ( (pindex= chainActive.LastTip()) != nullptr ) return pindex->GetHeight(); - else return(0); + else + return(0); + } + int64_t GetTipTime() + { + CBlockIndex *pindex; + if ( (pindex= chainActive.LastTip()) != nullptr ) + return pindex->nTime; + else + return(0); } void UpdatePreferredDownload(CNode* node, CNodeState* state) @@ -649,6 +656,7 @@ CBlockTreeDB *pblocktree = NULL; // Komodo globals #define KOMODO_ZCASH +#include "komodo_defs.h" #include "komodo.h" UniValue komodo_snapshot(int top) @@ -924,7 +932,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)) { // allow dust for cc chains + } else if (whichType != TX_CRYPTOCONDITION /*!IsCryptoConditionsEnabled()*/ && txout.IsDust(::minRelayTxFee)) { // allow dust for cc chains reason = "dust"; return false; } @@ -1043,8 +1051,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, const CScript& prevScript = prev.scriptPubKey; //printf("Previous script: %s\n", prevScript.ToString().c_str()); - if (!Solver(prevScript, whichType, vSolutions)) + bool iscltv; + // allow CLTV inputs: + if (!SolverCLTV(prevScript, whichType, vSolutions, iscltv)) return false; + int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); if (nArgsExpected < 0) return false; @@ -1453,7 +1464,7 @@ int32_t komodo_isnotaryvout(char *coinaddr,uint32_t tiptime) // from ac_private return(0); } -int32_t komodo_acpublic(uint32_t tiptime); +//int32_t komodo_acpublic(uint32_t tiptime); bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransaction& tx, CValidationState &state) { @@ -1814,7 +1825,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (limit > 0) { size_t n = tx.vin.size(); if (n > limit) { - LogPrint("mempool", "Dropping txid %s : too many transparent inputs %zu > limit %zu\n", tx.GetHash().ToString(), n, limit ); + LogPrint("mempool", "Dropping txid %s : too many transparent inputs %zu > limit %zu\n", tx.GetHash().ToString().c_str(), n, limit ); return state.Error("AcceptToMemoryPool: too many inputs"); } } @@ -1822,12 +1833,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa auto verifier = libzcash::ProofVerifier::Strict(); if ( ASSETCHAINS_SYMBOL[0] == 0 && komodo_validate_interest(tx,chainActive.LastTip()->GetHeight()+1,chainActive.LastTip()->GetMedianTimePast() + 777,0) < 0 ) { - fprintf(stderr,"AcceptToMemoryPool komodo_validate_interest failure\n"); + LogPrint("mempool-tx", "%s: komodo_validate_interest failed, tx: %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return state.Error("AcceptToMemoryPool: komodo_validate_interest failed"); } if (!CheckTransaction(tiptime,tx, state, verifier, 0, 0)) { + LogPrint("mempool", "%s: CheckTransaction failed, tx: %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return error("AcceptToMemoryPool: CheckTransaction failed"); // state must be already set } @@ -1835,13 +1847,14 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Check transaction contextually against the set of consensus rules which apply in the next block to be mined. if (!ContextualCheckTransaction(0,0,0,tx, state, nextBlockHeight, (dosLevel == -1) ? 10 : dosLevel)) { + LogPrint("mempool-tx", "%s: ContextualCheckTransaction failed, tx: %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return error("AcceptToMemoryPool: ContextualCheckTransaction failed"); // state must be already set } //fprintf(stderr,"addmempool 2\n"); // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) { - fprintf(stderr,"AcceptToMemoryPool coinbase as individual tx\n"); + LogPrint("mempool-tx", "%s: coinbase as individual tx not supported, tx: %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"), REJECT_INVALID, "coinbase"); } @@ -1849,7 +1862,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa string reason; if (Params().RequireStandard() && !IsStandardTx(tx, reason, nextBlockHeight)) { - //fprintf(stderr,"AcceptToMemoryPool reject nonstandard transaction: %s\nscriptPubKey: %s\n",reason.c_str(),tx.vout[0].scriptPubKey.ToString().c_str()); + LogPrint("mempool-tx","%s reject nonstandard transaction: reason %s tx: %s\n", __func__, reason.c_str(), HexStr(E_MARSHAL(ss << tx)).c_str()); return state.DoS(0,error("AcceptToMemoryPool: nonstandard transaction: %s", reason), REJECT_NONSTANDARD, reason); } @@ -1858,7 +1871,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // be mined yet. if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) { - //fprintf(stderr,"AcceptToMemoryPool reject non-final\n"); + LogPrint("mempool-tx", "%s: reject non final, tx: %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); } //fprintf(stderr,"addmempool 3\n"); @@ -1866,7 +1879,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa uint256 hash = tx.GetHash(); if (pool.exists(hash)) { - //fprintf(stderr,"already in mempool\n"); + LogPrint("mempool-tx", "%s: reject already in mempool, tx: %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return state.Invalid(false, REJECT_DUPLICATE, "already in mempool"); } @@ -1879,6 +1892,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (pool.mapNextTx.count(outpoint)) { // Disable replacement feature for now + LogPrint("mempool-tx", "%s: transaction replacement in mempool not allowed, tx: %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return state.Error("AcceptToMemoryPool: transaction replacement in mempool not allowed"); } } @@ -1912,14 +1926,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (view.HaveCoins(hash)) { //fprintf(stderr,"view.HaveCoins(hash) error\n"); + LogPrint("mempool-tx", "%s: already have coins, tx: %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return state.Invalid(false, REJECT_DUPLICATE, "already have coins"); } if (tx.IsCoinImport() || tx.IsPegsImport()) { // Inverse of normal case; if input exists, it's been spent - if (ExistsImportTombstone(tx, view)) + if (ExistsImportTombstone(tx, view)) { + LogPrint("mempool-tx", "%s: import tombstone exists, tx: %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return state.Invalid(false, REJECT_DUPLICATE, "import tombstone exists"); + } } else { @@ -1932,7 +1949,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { if (pfMissingInputs) *pfMissingInputs = true; - fprintf(stderr,"%s missing inputs for tx %s prevout.hash=%s\n", __func__, tx.GetHash().GetHex().c_str(), txin.prevout.hash.GetHex().c_str()); //TODO: remove + LogPrint("mempool-tx", "%s missing inputs for tx %s prevout.hash=%s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str(), txin.prevout.hash.GetHex().c_str()); return false; /* https://github.com/zcash/zcash/blob/master/src/main.cpp#L1490 @@ -1944,6 +1961,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (!view.HaveInputs(tx)) { //fprintf(stderr,"accept failure.1\n"); + LogPrint("mempool-tx", "%s inputs already spent for tx %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent"); } } @@ -1965,8 +1983,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa view.SetBackend(dummy); } // Check for non-standard pay-to-script-hash in inputs - if (Params().RequireStandard() && !AreInputsStandard(tx, view, consensusBranchId)) + if (Params().RequireStandard() && !AreInputsStandard(tx, view, consensusBranchId)) { + LogPrint("mempool-tx", "%s reject nonstandard transaction input for tx %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return state.Error("AcceptToMemoryPool: reject nonstandard transaction input"); + } // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction @@ -1984,8 +2004,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; double dPriority = view.GetPriority(tx, chainActive.Height()); - if ( nValueOut > 777777*COIN && KOMODO_VALUETOOBIG(nValueOut - 777777*COIN) != 0 ) // some room for blockreward and txfees + if ( nValueOut > 777777*COIN && KOMODO_VALUETOOBIG(nValueOut - 777777*COIN) != 0 ) { // some room for blockreward and txfees + LogPrint("mempool-tx", "%s GetValueOut too big for tx %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return state.DoS(100, error("AcceptToMemoryPool: GetValueOut too big"),REJECT_INVALID,"tx valueout is too big"); + } // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. @@ -2017,7 +2039,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (fLimitFree && nFees < txMinFee) { //fprintf(stderr,"accept failure.5\n"); - return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d",hash.ToString(), nFees, txMinFee),REJECT_INSUFFICIENTFEE, "insufficient fee"); + LogPrint("mempool-tx", "%s not enough fees for tx %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); + return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d", hash.ToString(), nFees, txMinFee),REJECT_INSUFFICIENTFEE, "insufficient fee"); } } @@ -2047,6 +2070,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) { //fprintf(stderr,"accept failure.7\n"); + LogPrint("mempool-tx", "%s free transaction rejected by rate limiter for tx %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), REJECT_INSUFFICIENTFEE, "rate limited free transaction"); } LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); @@ -2059,6 +2083,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa hash.ToString(), nFees, ::minRelayTxFee.GetFee(nSize) * 10000); LogPrint("mempool", errmsg.c_str()); + LogPrint("mempool-tx", "%s rejected %s for tx %s\n", __func__, errmsg.c_str(), HexStr(E_MARSHAL(ss << tx)).c_str()); return state.Error("AcceptToMemoryPool: " + errmsg); } //fprintf(stderr,"addmempool 6\n"); @@ -2067,10 +2092,11 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // This is done last to help prevent CPU exhaustion denial-of-service attacks. PrecomputedTransactionData txdata(tx); std::shared_ptr evalcodeChecker(new CCheckCCEvalCodes()); - if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId, evalcodeChecker)) + if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId, GetTime(), chainActive.LastTip()->GetHeight() + 1, evalcodeChecker)) // we can use GetTime() here, does not make big difference as this time is used to activate HF code for txns in mempool { //fprintf(stderr,"accept failure.9\n"); - return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString()); + LogPrint("mempool-tx", "%s ConnectInputs failed for tx %s\n", __func__, HexStr(E_MARSHAL(ss << tx)).c_str()); + return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString().c_str()); } // Check again against just the consensus-critical mandatory script @@ -2090,15 +2116,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } //fprintf(stderr,"addmempool 7\n"); - if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId, evalcodeChecker)) + if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId, GetTime(), chainActive.LastTip()->GetHeight() + 1, evalcodeChecker)) { if (flag != 0) KOMODO_CONNECTING = -1; return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); } - if (!ContextualCheckOutputs(tx, state, true, txdata, evalcodeChecker)) - return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ContextualCheckOutputs failed %s", hash.ToString());; + if (!ContextualCheckOutputs(tx, state, true, txdata, GetTime(), chainActive.LastTip()->GetHeight() + 1, evalcodeChecker)) + return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ContextualCheckOutputs failed %s", hash.ToString()); if (flag != 0) KOMODO_CONNECTING = -1; @@ -2226,16 +2252,17 @@ struct CompareBlocksByHeightMain else return(coins.vout[n].nValue); }*/ -bool myAddtomempool(const CTransaction &tx, CValidationState *pstate, bool fSkipExpiry) +bool myAddtomempool(const CTransaction &tx, CValidationState *pstate, bool *pMissingInputs, bool fSkipExpiry) { CValidationState state; if (!pstate) pstate = &state; - CTransaction Ltx; bool fMissingInputs,fOverrideFees = false; - if ( mempool.lookup(tx.GetHash(),Ltx) == 0 ) + CTransaction Ltx; + bool fOverrideFees = false; + if (mempool.lookup(tx.GetHash(), Ltx) == false) { if ( !fSkipExpiry ) - return(AcceptToMemoryPool(mempool, *pstate, tx, false, &fMissingInputs, !fOverrideFees, -1)); + return(AcceptToMemoryPool(mempool, *pstate, tx, false, pMissingInputs, !fOverrideFees, -1)); else return(CCTxFixAcceptToMemPoolUnchecked(mempool,tx)); } @@ -2467,12 +2494,6 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex,bool checkPOW) } //uint64_t komodo_moneysupply(int32_t height); -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -extern uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS+1], ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS+1], ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS+1]; -extern uint32_t ASSETCHAINS_MAGIC; -extern uint64_t ASSETCHAINS_LINEAR,ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY; -extern uint8_t ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE; -extern int32_t ASSETCHAINS_STAKED; CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { @@ -2785,19 +2806,16 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) UpdateCoins(tx, inputs, txundo, nHeight); } -bool CScriptCheck::operator()() { - if (vout!=0) - { - ServerTransactionSignatureChecker checker(ptxTo, n, amount, cacheStore, evalcodeChecker, *txdata); - if (checker.CheckCryptoCondition(scriptPubKey.GetCCV2SPK(),&error)!=1) - { - return ::error("CScriptCheck(): %s:%d CC validation failed: %s", ptxTo->GetHash().ToString(), n, ScriptErrorString(error)); - } - } - else - { - const CScript &scriptSig = ptxTo->vin[n].scriptSig; - ServerTransactionSignatureChecker checker(ptxTo, n, amount, cacheStore, evalcodeChecker, *txdata); +bool CScriptCheck::operator()() +{ + if (vout != 0) { //check cc output + ServerTransactionSignatureChecker checker(ptxTo, n, amount, cacheStore, nTime, nHeight, evalcodeChecker, *txdata); + if (checker.CheckCryptoConditionSpk(scriptPubKey.GetCCV2SPK(), &error) != 1) { + return ::error("CScriptCheck(): %s:%d CC output validation failed: %s", ptxTo->GetHash().ToString(), n, ScriptErrorString(error)); + } + } else { // check cc input + const CScript& scriptSig = ptxTo->vin[n].scriptSig; + ServerTransactionSignatureChecker checker(ptxTo, n, amount, cacheStore, nTime, nHeight, evalcodeChecker, *txdata); if (!VerifyScript(scriptSig, scriptPubKey, nFlags, checker, consensusBranchId, &error)) { return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), n, ScriptErrorString(error)); } @@ -2805,32 +2823,40 @@ bool CScriptCheck::operator()() { return true; } -int GetSpendHeight(const CCoinsViewCache& inputs) +int GetSpendHeight(const CCoinsViewCache& inputs) // dimxy why inputs? { LOCK(cs_main); CBlockIndex* pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; return pindexPrev->GetHeight() + 1; } +/// @author dimxy +/// get estimated next block time (assuming it would be a spending block) +/// this value would be used for timestamp-activating HF code in coins cache code +int64_t GetSpendTime(const CCoinsViewCache& coins) +{ + LOCK(cs_main); + CBlockIndex* pindexPrev = mapBlockIndex.find(coins.GetBestBlock())->second; + return pindexPrev->GetMedianTimePast(); +} + namespace Consensus { - bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams) + bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, int64_t nSpendTime, const Consensus::Params& consensusParams) { // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!inputs.HaveInputs(tx)) return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); - // are the JoinSplit's requirements met? if (!inputs.HaveJoinSplitRequirements(tx)) return state.Invalid(error("CheckInputs(): %s JoinSplit requirements not met", tx.GetHash().ToString())); - CAmount nValueIn = 0; CAmount nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { if (tx.IsPegsImport() && i==0) { - nValueIn=GetCoinImportValue(tx); + nValueIn=GetCoinImportValue(tx, nSpendTime, nSpendHeight); continue; } const COutPoint &prevout = tx.vin[i].prevout; @@ -2924,12 +2950,14 @@ bool ContextualCheckInputs( PrecomputedTransactionData& txdata, const Consensus::Params& consensusParams, uint32_t consensusBranchId, + int64_t nTime, + int32_t nHeight, std::shared_ptr evalcodeChecker, std::vector *pvChecks) { if (!tx.IsMint()) { - if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) { + if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), GetSpendTime(inputs), consensusParams)) { return false; } @@ -2951,7 +2979,7 @@ bool ContextualCheckInputs( assert(coins); // Verify signature - CScriptCheck check(*coins, tx, i, flags, cacheStore, consensusBranchId, evalcodeChecker, &txdata); + CScriptCheck check(*coins, tx, i, flags, cacheStore, consensusBranchId, nTime, nHeight, evalcodeChecker, &txdata); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -2964,7 +2992,7 @@ bool ContextualCheckInputs( // avoid splitting the network between upgraded and // non-upgraded nodes. CScriptCheck check2(*coins, tx, i, - flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, consensusBranchId, evalcodeChecker, &txdata); + flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, consensusBranchId, nTime, nHeight, evalcodeChecker, &txdata); if (check2()) return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); } @@ -2975,7 +3003,10 @@ bool ContextualCheckInputs( // as to the correct behavior - we may want to continue // peering with non-upgraded nodes even after a soft-fork // super-majority vote has passed. - return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); + return state.DoS(100,false, REJECT_INVALID, + strprintf("mandatory-script-verify-flag-failed (%s)%s", + ScriptErrorString(check.GetScriptError()), + evalcodeChecker->lastEvalErrorState.IsValid() == false ? strprintf(", eval errcode=%d reason=%s", evalcodeChecker->lastEvalErrorState.GetRejectCode(), evalcodeChecker->lastEvalErrorState.GetRejectReason()) : "")); } } } @@ -2984,7 +3015,7 @@ bool ContextualCheckInputs( if (tx.IsCoinImport() || tx.IsPegsImport()) { LOCK(cs_main); - ServerTransactionSignatureChecker checker(&tx, 0, 0, false, NULL,txdata); + ServerTransactionSignatureChecker checker(&tx, 0, 0, false, nTime, nHeight, NULL, txdata); return VerifyCoinImport(tx.vin[0].scriptSig, checker, state); } @@ -2996,33 +3027,43 @@ bool ContextualCheckOutputs( CValidationState &state, bool fScriptChecks, PrecomputedTransactionData& txdata, + int64_t nTime, + int32_t nHeight, std::shared_ptr evalcodeChecker, std::vector *pvChecks) { - if (!tx.IsCoinBase()) - { - if (pvChecks) - pvChecks->reserve(tx.vout.size()); + if (pvChecks) + pvChecks->reserve(tx.vout.size()); - if (fScriptChecks) { - for (unsigned int i = 0; i < tx.vout.size(); i++) { - if (tx.vout[i].scriptPubKey.IsPayToCCV2() ) + if (fScriptChecks) + { + for (unsigned int i = 0; i < tx.vout.size(); i++) + { + + if (tx.vout[i].scriptPubKey.IsPayToCCV2() ) + { + // check if secp256hash and eval param in action: + int subversion = CC_MixedModeSubVersion(tx.vout[i].scriptPubKey[0]); + if (subversion >= CC_MIXED_MODE_SECHASH_SUBVER_1 && !CCUpgrades::IsUpgradeActive(nTime, nHeight, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)) { - CScriptCheck check(tx.vout[i].scriptPubKey, tx.vout[i].nValue, tx, i, evalcodeChecker, &txdata); - if (pvChecks) - { - pvChecks->push_back(CScriptCheck()); - check.swap(pvChecks->back()); - } - else if (!check()) - { - return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); - } + return state.DoS(100,false, REJECT_INVALID, std::string("cc v2 subversion 1 or more not yet enabled")); + } + CScriptCheck check(tx.vout[i].scriptPubKey, tx.vout[i].nValue, tx, i, nTime, nHeight, evalcodeChecker, &txdata); + if (pvChecks) + { + pvChecks->push_back(CScriptCheck()); + check.swap(pvChecks->back()); + } + else if (!check()) + { + return state.DoS(100,false, REJECT_INVALID, + strprintf("mandatory-script-verify-flag-failed (%s)%s", + ScriptErrorString(check.GetScriptError()), + evalcodeChecker->lastEvalErrorState.IsValid() == false ? strprintf(", eval errcode=%d reason=%s", evalcodeChecker->lastEvalErrorState.GetRejectCode(), evalcodeChecker->lastEvalErrorState.GetRejectReason()) : "")); } } } } - return true; } @@ -3854,9 +3895,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin //fprintf(stderr, "tx.%s nFees.%li interest.%li\n", tx.GetHash().ToString().c_str(), stakeTxValue, interest); std::vector vChecks; - if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, txdata[i], chainparams.GetConsensus(), consensusBranchId, evalcodeChecker, nScriptCheckThreads ? &vChecks : NULL)) + if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, txdata[i], chainparams.GetConsensus(), consensusBranchId, pindex->GetBlockTime(), pindex->GetHeight(), evalcodeChecker, nScriptCheckThreads ? &vChecks : NULL)) return false; - if (!ContextualCheckOutputs(tx, state, fExpensiveChecks, txdata[i], evalcodeChecker, nScriptCheckThreads ? &vChecks : NULL)) + control.Add(vChecks); + } + + { // check tx outputs including coinbases + std::vector vChecks; + if (!ContextualCheckOutputs(tx, state, fExpensiveChecks, txdata[i], pindex->GetBlockTime(), pindex->GetHeight(), evalcodeChecker, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } @@ -5533,7 +5579,7 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C if ( tx.IsCoinBase() || !tx.vjoinsplit.empty() || !tx.vShieldedSpend.empty() || (i == block.vtx.size()-1 && komodo_isPoS((CBlock *)&block,height,0) != 0) ) continue; Tx = tx; - if ( myAddtomempool(Tx, &state, true) == false ) // happens with out of order tx in block on resync + if ( myAddtomempool(Tx, &state, nullptr, true) == false ) // happens with out of order tx in block on resync { //LogPrintf("Rejected by mempool, reason: .%s.\n", state.GetRejectReason().c_str()); // take advantage of other checks, but if we were only rejected because it is a valid staking @@ -6921,7 +6967,7 @@ bool InitBlockIndex() { pblocktree->WriteFlag("spentindex", fSpentIndex); fprintf(stderr,"fAddressIndex.%d/%d fSpentIndex.%d/%d\n",fAddressIndex,DEFAULT_ADDRESSINDEX,fSpentIndex,DEFAULT_SPENTINDEX); - fUnspentCCIndex = GetBoolArg("-unspentccindex", false); + fUnspentCCIndex = GetBoolArg("-unspentccindex", DEFAULT_UNSPENTCCINDEX); pblocktree->WriteFlag("unspentccindex", fUnspentCCIndex); fprintf(stderr, "fUnspentCCIndex.%d\n", fUnspentCCIndex); @@ -7453,10 +7499,18 @@ void static ProcessGetData(CNode* pfrom) // Send stream from relay memory bool pushed = false; { - LOCK(cs_mapRelay); - map::iterator mi = mapRelay.find(inv); - if (mi != mapRelay.end()) { - pfrom->PushMessage(inv.GetCommand(), (*mi).second); + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + bool found = false; + { + LOCK(cs_mapRelay); + map::iterator mi = mapRelay.find(inv); + if (mi != mapRelay.end()) { + ss = ((*mi).second); + found = true; + } + } + if (found) { + pfrom->PushMessage(inv.GetCommand(), ss); pushed = true; } } @@ -7505,7 +7559,7 @@ void static ProcessGetData(CNode* pfrom) bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { - int32_t nProtocolVersion; + //int32_t nProtocolVersion; const CChainParams& chainparams = Params(); LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); //if ( KOMODO_NSPV_SUPERLITE ) @@ -7550,9 +7604,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return false; } + const int64_t nTipTime = GetTipTime(); + const int nHeight = GetHeight(); // Reject incoming connections from nodes that don't know about the current epoch const Consensus::Params& params = Params().GetConsensus(); - auto currentEpoch = CurrentEpoch(GetHeight(), params); + auto currentEpoch = CurrentEpoch(nHeight, params); if (nVersion < params.vUpgrades[currentEpoch].nProtocolVersion) { LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, nVersion); @@ -7563,6 +7619,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return false; } + // check min cc version + if (nVersion < GetCurrentUpgradeInfo(nTipTime, nHeight, CCUpgrades::GetUpgrades()).nProtocolVersion) + { + LogPrintf("peer=%d using obsolete version %i, needed %d; disconnecting by ccupgrades\n", pfrom->id, nVersion, GetCurrentUpgradeInfo(nTipTime, nHeight, CCUpgrades::GetUpgrades()).nProtocolVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", + GetCurrentUpgradeInfo(nTipTime, nHeight, CCUpgrades::GetUpgrades()).nProtocolVersion)); + pfrom->fDisconnect = true; + return false; + } + if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; if (!vRecv.empty()) { @@ -7877,7 +7944,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->vAddrToSend.clear(); vector vAddr = addrman.GetAddr(); BOOST_FOREACH(const CAddress &addr, vAddr) - pfrom->PushAddress(addr); + pfrom->PushAddress(addr); } // temporary optional nspv message processing else if ((nLocalServices & NODE_NSPV) && @@ -8572,7 +8639,14 @@ bool ProcessMessages(CNode* pfrom) bool fRet = false; try { - fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime); + +#ifdef ENABLE_WEBSOCKETS + if (pfrom->hSocket != INVALID_SOCKET || !(fRet = ws::ProcessWsMessage(pfrom, strCommand, vRecv, msg.nTime))) { +#endif + fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime); +#ifdef ENABLE_WEBSOCKETS + } +#endif boost::this_thread::interruption_point(); } catch (const std::ios_base::failure& e) diff --git a/src/main.h b/src/main.h index 7d67119f8ab..5a618d69221 100644 --- a/src/main.h +++ b/src/main.h @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2022 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -65,6 +65,8 @@ class CValidationInterface; class CValidationState; class PrecomputedTransactionData; +class CCheckCCEvalCodes; + struct CNodeStateStats; #define DEFAULT_MEMPOOL_EXPIRY 1 #define _COINBASE_MATURITY 100 @@ -121,15 +123,15 @@ static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60; static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60; -/** Default NSPV support enabled */ -#define DEFAULT_NSPV_TOKEL true -static const bool DEFAULT_NSPV_PROCESSING = DEFAULT_NSPV_TOKEL; +/** Default NSPV support enabled for Tokel */ +static const bool DEFAULT_NSPV_PROCESSING = true; -//static const bool DEFAULT_ADDRESSINDEX = false; -//static const bool DEFAULT_SPENTINDEX = false; #define DEFAULT_ADDRESSINDEX (GetArg("-ac_cc",0) != 0 || GetArg("-ac_ccactivate",0) != 0) #define DEFAULT_SPENTINDEX (GetArg("-ac_cc",0) != 0 || GetArg("-ac_ccactivate",0) != 0) +/** Default unspent cc enabled for Tokel */ +static const bool DEFAULT_UNSPENTCCINDEX = true; + static const bool DEFAULT_TIMESTAMPINDEX = false; static const unsigned int DEFAULT_DB_MAX_OPEN_FILES = 1000; static const bool DEFAULT_DB_COMPRESSION = true; @@ -665,36 +667,6 @@ struct CDiskTxPos : public CDiskBlockPos CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree); -class CCheckCCEvalCodes -{ - //! The set of evalcodes that are already processed in CC validation. - std::map > evalcodes; - - //! Mutex to protect evalcodes map - boost::mutex mutex_eval; - -public: - void MarkEvalCode(uint256 txid,uint8_t ecode) - { - boost::unique_lock lock(mutex_eval); - auto search = evalcodes.find(txid); - if (search==evalcodes.end()) - { - std::set tmp; - tmp.insert(ecode); - evalcodes[txid]=tmp; - } - else search->second.insert(ecode); - } - - bool CheckEvalCode(uint256 txid, uint8_t ecode) - { - boost::unique_lock lock(mutex_eval); - auto search = evalcodes.find(txid); - return search==evalcodes.end()?false:(search->second.find(ecode)!=search->second.end()); - } -}; - /** * Check transaction inputs, and make sure any * pay-to-script-hash transactions are evaluating IsStandard scripts @@ -737,14 +709,22 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& ma * instead of being performed inline. */ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks, - unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, - const Consensus::Params& consensusParams, uint32_t consensusBranchId, std::shared_ptr evalcodeChecker, + unsigned int flags, + bool cacheStore, + PrecomputedTransactionData& txdata, + const Consensus::Params& consensusParams, + uint32_t consensusBranchId, + int64_t nTime, + int32_t nHeight, + std::shared_ptr evalcodeChecker, std::vector *pvChecks = NULL); bool ContextualCheckOutputs( const CTransaction& tx, CValidationState &state, bool fScriptChecks, PrecomputedTransactionData& txdata, + int64_t nTime, + int32_t nHeight, std::shared_ptr evalcodeChecker, std::vector *pvChecks = NULL); @@ -773,7 +753,7 @@ namespace Consensus { * This does not modify the UTXO set. This does not check scripts and sigs. * Preconditions: tx.IsCoinBase() is false. */ -bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams); +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, int64_t nSpendTime, const Consensus::Params& consensusParams); } // namespace Consensus @@ -808,22 +788,24 @@ class CScriptCheck CScript scriptPubKey; CAmount amount; const CTransaction *ptxTo; - unsigned int n; + unsigned int n; // vin/vout index, since ccv2 unused for cc validation (it always validates the whole cc tx and does not re-enter validation for each vin) unsigned int nFlags; bool cacheStore; uint32_t consensusBranchId; + int64_t nTime; + int32_t nHeight; ScriptError error; PrecomputedTransactionData *txdata; std::shared_ptr evalcodeChecker; bool vout; public: - CScriptCheck(): amount(0), ptxTo(0), n(0), nFlags(0), cacheStore(false), consensusBranchId(0), error(SCRIPT_ERR_UNKNOWN_ERROR), vout(false) {} - CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nIn, unsigned int nFlagsIn, bool cacheIn, uint32_t consensusBranchIdIn, std::shared_ptr evalcodeCheckerIn,PrecomputedTransactionData* txdataIn) : + CScriptCheck(): amount(0), ptxTo(0), n(0), nFlags(0), cacheStore(false), consensusBranchId(0), nTime(0LL), nHeight(0), error(SCRIPT_ERR_UNKNOWN_ERROR), vout(false) {} + CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nIn, unsigned int nFlagsIn, bool cacheIn, uint32_t consensusBranchIdIn, int32_t nTimeIn, int32_t nHeightIn, std::shared_ptr evalcodeCheckerIn,PrecomputedTransactionData* txdataIn) : scriptPubKey(CCoinsViewCache::GetSpendFor(&txFromIn, txToIn.vin[nIn])), amount(txFromIn.vout[txToIn.vin[nIn].prevout.n].nValue), - ptxTo(&txToIn), n(nIn), nFlags(nFlagsIn), cacheStore(cacheIn), consensusBranchId(consensusBranchIdIn), error(SCRIPT_ERR_UNKNOWN_ERROR), evalcodeChecker(evalcodeCheckerIn),txdata(txdataIn), vout(false) { } - CScriptCheck(const CScript& scriptPubKeyIn, const CAmount& amountIn, const CTransaction& txToIn, unsigned int nIn, std::shared_ptr evalcodeCheckerIn,PrecomputedTransactionData* txdataIn) : - scriptPubKey(scriptPubKeyIn), amount(amountIn), ptxTo(&txToIn), n(nIn), nFlags(0), cacheStore(false), consensusBranchId(0), + ptxTo(&txToIn), n(nIn), nFlags(nFlagsIn), cacheStore(cacheIn), consensusBranchId(consensusBranchIdIn), nTime(nTimeIn), nHeight(nHeightIn), error(SCRIPT_ERR_UNKNOWN_ERROR), evalcodeChecker(evalcodeCheckerIn),txdata(txdataIn), vout(false) { } + CScriptCheck(const CScript& scriptPubKeyIn, const CAmount& amountIn, const CTransaction& txToIn, unsigned int nIn, int32_t nTimeIn, int32_t nHeightIn, std::shared_ptr evalcodeCheckerIn,PrecomputedTransactionData* txdataIn) : + scriptPubKey(scriptPubKeyIn), amount(amountIn), ptxTo(&txToIn), n(nIn), nFlags(0), cacheStore(false), consensusBranchId(0), nTime(nTimeIn), nHeight(nHeightIn), error(SCRIPT_ERR_UNKNOWN_ERROR), evalcodeChecker(evalcodeCheckerIn), txdata(txdataIn), vout(true) { } bool operator()(); @@ -835,6 +817,8 @@ class CScriptCheck std::swap(nFlags, check.nFlags); std::swap(cacheStore, check.cacheStore); std::swap(consensusBranchId, check.consensusBranchId); + std::swap(nTime, check.nTime); + std::swap(nHeight, check.nHeight); std::swap(error, check.error); std::swap(txdata, check.txdata); std::swap(vout,check.vout); @@ -995,6 +979,10 @@ extern CBlockTreeDB *pblocktree; */ int GetSpendHeight(const CCoinsViewCache& inputs); +/** get next block time for coins */ +int64_t GetSpendTime(const CCoinsViewCache& coins); + + uint64_t CalculateCurrentUsage(); /** Return a CMutableTransaction with contextual default values based on set of consensus rules at height */ diff --git a/src/miner.cpp b/src/miner.cpp index 7e44511d49f..fc64e480152 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -64,6 +64,9 @@ #endif #include +#include "komodo_defs.h" +#include "cc/CCinclude.h" + using namespace std; ////////////////////////////////////////////////////////////////////////////// @@ -71,6 +74,12 @@ using namespace std; // BitcoinMiner // + +extern CCriticalSection cs_metrics; + +uint32_t Mining_start,Mining_height; +int32_t My_notaryid = -1; + // // Unconfirmed transactions in the memory pool often depend on other // transactions in the memory pool. When we select transactions from the @@ -133,34 +142,6 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams); } } -#include "komodo_defs.h" -#include "cc/CCinclude.h" - -extern CCriticalSection cs_metrics; -void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len); - -uint32_t Mining_start,Mining_height; -int32_t My_notaryid = -1; -int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp); -int32_t komodo_pax_opreturn(int32_t height,uint8_t *opret,int32_t maxsize); -int32_t komodo_baseid(char *origbase); -int32_t komodo_longestchain(); -int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_t nTime,int32_t dispflag); -int64_t komodo_block_unlocktime(uint32_t nHeight); -uint64_t komodo_commission(const CBlock *block,int32_t height); -int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig, uint256 merkleroot); -int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey &pk); -uint256 komodo_calcmerkleroot(CBlock *pblock, uint256 prevBlockHash, int32_t nHeight, bool fNew, CScript scriptPubKey); -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); -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); -CScript komodo_mineropret(int32_t nHeight); -bool komodo_appendACscriptpub(); -CScript komodo_makeopret(CBlock *pblock, bool fNew); int32_t komodo_waituntilelegible(uint32_t blocktime, int32_t stakeHeight, uint32_t delay) { @@ -340,7 +321,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 std::vector TMP_NotarisationNotaries; if (tx.IsCoinImport()) { - CAmount nValueIn = GetCoinImportValue(tx); // burn amount + CAmount nValueIn = GetCoinImportValue(tx, pblock->nTime, nHeight); // burn amount nTotalIn += nValueIn; dPriority += (double)nValueIn * 1000; // flat multiplier... max = 1e16. } else { @@ -353,7 +334,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 { if (tx.IsPegsImport() && txin.prevout.n==10e8) { - CAmount nValueIn = GetCoinImportValue(tx); // burn amount + CAmount nValueIn = GetCoinImportValue(tx, pblock->nTime, nHeight); // burn amount nTotalIn += nValueIn; dPriority += (double)nValueIn * 1000; // flat multiplier... max = 1e16. continue; @@ -584,8 +565,8 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 // create only contains transactions that are valid in new blocks. CValidationState state; PrecomputedTransactionData txdata(tx); - std::shared_ptr evalcodeChecker(new CCheckCCEvalCodes()); - if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId, evalcodeChecker)) + std::shared_ptr evalcodeChecker(new CCheckCCEvalCodes()); // evalcodeChecker pblock->nTime, nHeight are not used as no cc checks are performed (only MANDATORY_SCRIPT_VERIFY_FLAGS) + if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId, pblock->nTime, nHeight, evalcodeChecker)) { //fprintf(stderr,"context failure\n"); continue; @@ -1136,13 +1117,10 @@ static bool ProcessBlockFound(CBlock* pblock) return true; } -int32_t komodo_baseid(char *origbase); -int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,uint32_t *blocktimes,int32_t *nonzpkeysp,int32_t height); -arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc,int32_t newStakerActive); -int32_t FOUND_BLOCK,KOMODO_MAYBEMINED; -extern int32_t KOMODO_LASTMINED,KOMODO_INSYNC; + +int32_t FOUND_BLOCK, KOMODO_MAYBEMINED; int32_t roundrobin_delay; -arith_uint256 HASHTarget,HASHTarget_POW; +arith_uint256 HASHTarget, HASHTarget_POW; // wait for peers to connect void waitForPeers(const CChainParams &chainparams) @@ -1191,19 +1169,17 @@ void waitForPeers(const CChainParams &chainparams) } #ifdef ENABLE_WALLET -CBlockIndex *get_chainactive(int32_t height) +CBlockIndex* get_chainactive(int32_t height) { - if ( chainActive.LastTip() != 0 ) - { - if ( height <= chainActive.LastTip()->GetHeight() ) - { - LOCK(cs_main); - return(chainActive[height]); + LOCK(cs_main); // moved lock at the begininng as getting the vector size is unsafe in the multithread env + if (chainActive.LastTip() != 0) { + if (height <= chainActive.LastTip()->GetHeight()) { + return (chainActive[height]); } // else fprintf(stderr,"get_chainactive height %d > active.%d\n",height,chainActive.Tip()->GetHeight()); } //fprintf(stderr,"get_chainactive null chainActive.Tip() height %d\n",height); - return(0); + return (0); } /* diff --git a/src/net.cpp b/src/net.cpp index 5fe1660254f..fdd345d5a06 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1439,6 +1439,13 @@ void ThreadOpenConnections() if (!addr.IsValid() || setConnected.count(addr.GetGroup(addrman.m_asmap)) || IsLocal(addr)) break; +#ifdef ENABLE_WEBSOCKETS + // do not connect to client nodes (looks like this should be added for non-websocket version too): + if ((addr.nServices & NODE_NETWORK) == 0) { + continue; + } +#endif + // If we didn't find an appropriate destination after trying 100 addresses fetched from addrman, // stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates // already-connected network ranges, ...) before trying new addrman addresses. @@ -2130,6 +2137,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa fInbound = fInboundIn; fNetworkNode = false; fSuccessfullyConnected = false; + fNspvConnected = false; fDisconnect = false; nRefCount = 0; nSendSize = 0; @@ -2145,6 +2153,8 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa nPingUsecTime = 0; fPingQueued = false; nMinPingUsecTime = std::numeric_limits::max(); + memset(nspvdata, '\0', sizeof(nspvdata)); + nLastWsAddrTime = 0LL; { LOCK(cs_nLastNodeId); @@ -2260,9 +2270,15 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) ssSend.GetAndClear(*it); nSendSize += (*it).size(); - // If write queue empty, attempt "optimistic write" - if (it == vSendMsg.begin()) - SocketSendData(this); +#ifdef ENABLE_WEBSOCKETS + if (this->hSocket != INVALID_SOCKET) { +#endif + // If write queue empty, attempt "optimistic write" + if (it == vSendMsg.begin()) + SocketSendData(this); +#ifdef ENABLE_WEBSOCKETS + } +#endif LEAVE_CRITICAL_SECTION(cs_vSend); } diff --git a/src/net.h b/src/net.h index e1f16972b67..3383cdc1990 100644 --- a/src/net.h +++ b/src/net.h @@ -284,7 +284,11 @@ class CNode int64_t nLastRecv; int64_t nTimeConnected; int64_t nTimeOffset; - uint32_t prevtimes[16],dexlastping; + struct { + uint32_t prevtime; + uint32_t nreqs; + } nspvdata[32]; + uint32_t dexlastping; // Address of this peer CAddress addr; // Bind address of our side of the connection @@ -304,6 +308,7 @@ class CNode bool fInbound; bool fNetworkNode; bool fSuccessfullyConnected; + bool fNspvConnected; bool fDisconnect; // We use fRelayTxes for two purposes - // a) it allows us to not relay tx invs before receiving the peer's version message @@ -340,6 +345,7 @@ class CNode CRollingBloomFilter addrKnown; bool fGetAddr; std::set setKnown; + int64_t nLastWsAddrTime; // inventory based relay mruset setInventoryKnown; diff --git a/src/protocol.h b/src/protocol.h index b7ad483ace2..cd480e10bb4 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -81,19 +81,19 @@ class CMessageHeader }; /** nServices flags */ -enum { +enum : uint64_t { // NODE_NETWORK means that the node is capable of serving the block chain. It is currently // set by all Bitcoin Core nodes, and is unset by SPV clients or other peers that just want // network services but don't provide them. - NODE_NETWORK = (1 << 0), + NODE_NETWORK = (1ULL << 0), // NODE_BLOOM means the node is capable and willing to handle bloom-filtered connections. // Zcash nodes used to support this by default, without advertising this bit, // but no longer do as of protocol version 170004 (= NO_BLOOM_VERSION) - NODE_BLOOM = (1 << 2), + NODE_BLOOM = (1ULL << 2), - NODE_NSPV = (1 << 30), - NODE_ADDRINDEX = (1 << 29), - NODE_SPENTINDEX = (1 << 28), + NODE_NSPV = (1ULL << 30), + NODE_ADDRINDEX = (1ULL << 29), + NODE_SPENTINDEX = (1ULL << 28), // 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 @@ -102,6 +102,9 @@ enum { // collisions and other cases where nodes may be advertising a service they // do not actually support. Other service bits should be allocated via the // BIP process. + + NODE_WEBSOCKETS = (1ULL << 31), // nspv listening on websockets + NODE_WEBSOCKETS_TLS = (1ULL << 32) // websockets tls }; /** A CService with information about it as peer */ diff --git a/src/rest.cpp b/src/rest.cpp index 3fb87f8f386..63de162c8fb 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -71,12 +71,6 @@ struct CCoin { } }; -extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); -extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false); -extern UniValue mempoolInfoToJSON(); -extern UniValue mempoolToJSON(bool fVerbose = false); -extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); -extern UniValue blockheaderToJSON(const CBlockIndex* blockindex); static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, string message) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 565a5bfb92d..c766aa8687c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -46,15 +46,11 @@ #include "cc/CCinclude.h" #include "cc/CCPrices.h" -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); #include "komodo_defs.h" #include "komodo_structs.h" +using namespace std; + double GetDifficultyINTERNAL(const CBlockIndex* blockindex, bool networkDifficulty) { // Floating point number that is a multiple of the minimum difficulty, @@ -288,7 +284,7 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) return result; } -UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) +UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails) { UniValue result(UniValue::VOBJ); uint256 notarized_hash, notarized_desttxid; int32_t prevMoMheight, notarized_height; @@ -451,7 +447,7 @@ bool mytxid_inmempool(uint256 txid) return(false); } -UniValue mempoolToJSON(bool fVerbose = false) +UniValue mempoolToJSON(bool fVerbose) { if (fVerbose) { @@ -1123,8 +1119,6 @@ UniValue notaries(const UniValue& params, bool fHelp, const CPubKey& mypk) return ret; } -int32_t komodo_pending_withdraws(char *opretstr); -int32_t pax_fiatstatus(uint64_t *available, uint64_t *deposited, uint64_t *issued, uint64_t *withdrawn, uint64_t *approved, uint64_t *redeemed, char *base); extern char CURRENCIES[][8]; UniValue paxpending(const UniValue& params, bool fHelp, const CPubKey& mypk) diff --git a/src/rpc/ccutilsrpc.cpp b/src/rpc/ccutilsrpc.cpp index c4bf88f7bd4..2f994b5ba4b 100644 --- a/src/rpc/ccutilsrpc.cpp +++ b/src/rpc/ccutilsrpc.cpp @@ -28,6 +28,8 @@ #include "sync_ext.h" #include "../main.h" #include "../cc/CCinclude.h" +#include "../cc/CCfaucet.h" + using namespace std; @@ -90,12 +92,246 @@ UniValue listccunspents(const UniValue& params, bool fHelp, const CPubKey& mypk) } +// a helper function for nspv clients: creates a tx and add normal inputs for the requested amount +UniValue createtxwithnormalinputs(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || (params.size() < 1 || params.size() > 2)) + { + string msg = "createtxwithnormalinputs amount [pubkey]\n" + "\nReturns a new tx with added normal inputs and previous txns. Note that the caller must add the change output\n" + "\nArguments:\n" + //"address which utxos are added from\n" + "amount (in satoshi) which will be added as normal inputs (equal or more)\n" + "pubkey optional, if not set -pubkey used\n" + "Result: json object with created tx and added vin txns\n\n"; + throw runtime_error(msg); + } + /*std::string address = params[0].get_str(); + if (!CBitcoinAddress(address.c_str()).IsValid()) + throw runtime_error("address invalid");*/ + CAmount amount = atoll(params[0].get_str().c_str()); + if (amount <= 0) + throw runtime_error("amount invalid"); + + CPubKey usedpk = remotepk; + if (params.size() >= 2) { + CPubKey pk = pubkey2pk(ParseHex(params[1].get_str())); + if (!pk.IsValid()) + throw runtime_error("pubkey invalid"); + usedpk = pk; + } + + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + mtx.nLockTime = komodo_next_tx_locktime(); + std::vector vintxns; + CAmount added = AddNormalinputsRemote(mtx, usedpk, amount, CC_MAXVINS, &vintxns); + if (added < amount) + throw runtime_error("could not find normal inputs"); + + for (auto const & vin : mtx.vin) { + CTransaction tx; + uint256 hashBlock; + if (myGetTransaction(vin.prevout.hash, tx, hashBlock)) + vintxns.push_back(tx); + } + UniValue result (UniValue::VOBJ); + UniValue array (UniValue::VARR); + + result.pushKV("txhex", HexStr(E_MARSHAL(ss << mtx))); + for (auto const &vtx : vintxns) + array.push_back(HexStr(E_MARSHAL(ss << vtx))); + result.pushKV("previousTxns", array); + return result; +} + +// helper for nspv clients, to load several txns by their txids +UniValue gettransactionsmany(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || params.size() < 1 || params.size() > 0x1000) + { + string msg = "gettransactionsmany txid1 txid2 ...\n" + "\nReturns a list of confirmed transactions for the given txids.\n" + "\nArguments:\n" + "txid1 txid2... - txids to load, with max number of no more than 4096\n" + "Result: json object with txns in hex and optional list of txids that could not be loaded\n\n"; + throw runtime_error(msg); + } + + UniValue result(UniValue::VOBJ); + UniValue txns(UniValue::VARR); + UniValue notloaded(UniValue::VARR); + + for (size_t i = 0; i < params.size(); i ++) + { + uint256 txid = Parseuint256(params[i].get_str().c_str()); + if (txid.IsNull()) + throw std::runtime_error("txid invalid for i=" + std::to_string(i)); + + CTransaction tx; + uint256 hashBlock; + if (myGetTransaction(txid, tx, hashBlock) && !hashBlock.IsNull()) { + UniValue elem(UniValue::VOBJ); + elem.pushKV("tx", HexStr(E_MARSHAL(ss << tx))); + + LOCK(cs_main); + CBlockIndex *pindex = komodo_getblockindex(hashBlock); + if (pindex) { + CNetworkBlockHeader nethdr = pindex->GetBlockHeader(); + elem.pushKV("blockHeader", HexStr(E_MARSHAL(ss << nethdr))); + elem.pushKV("blockHeight", pindex->GetHeight()); + elem.pushKV("blockHash", hashBlock.GetHex()); + } + txns.push_back(elem); + } + else + notloaded.push_back(txid.GetHex()); + } + result.pushKV("transactions", txns); + if (!notloaded.empty()) + result.pushKV("notloaded", notloaded); + + return result; +} + +// helper for testing, returns index key for a cryptoconditon scriptPubKey +UniValue getindexkeyforcc(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || params.size() != 2) + { + string msg = "getindexkeyforcc cc-as-json is-mixed\n" + "\nReturns indexing key (formely cc address) for scriptPubKey made from a cryptocondition\n" + "\nArguments:\n" + //"address which utxos are added from\n" + "cc-as-json cryptocondition in json\n" + "is-mixed is mixed mode, true or false" + "Result: indexing key\n\n" + "Sample:\n" + "getindexkeyforcc \'{ \"type\": \"threshold-sha-256\", \"threshold\": 2, \"subfulfillments\":" + "[{\"type\":\"eval-sha-256\",\"code\":\"9A\"}, {\"type\":\"threshold-sha-256\", \"threshold\":1," + "\"subfulfillments\":[{ \"type\": \"secp256k1-sha-256\", \"publicKey\": \"03682b255c40d0cde8faee381a1a50bbb89980ff24539cb8518e294d3a63cefe12\" }] }] }\' true\n\n" + ; + throw std::runtime_error(msg); + } + + char err[128];// = ""; + CCwrapper cc = cc_conditionFromJSONString(params[0].get_str().c_str(), err); + if (cc == nullptr) + throw std::runtime_error(std::string("could not create cryptocondition: ") + err); + bool ismixed = false; + if (params[1].get_str() == "true") + ismixed = true; + else if (params[1].get_str() == "false") + ismixed = false; + else + throw std::runtime_error(std::string("is-mixed must be true or false")); + + UniValue result(UniValue::VARR); + if (ismixed) { + for (CC_SUBVER ccSubVer = CC_MIXED_MODE_SUBVER_0; ccSubVer <= CC_MIXED_MODE_SUBVER_MAX; ccSubVer = (CC_SUBVER)(ccSubVer+1)) { + CScript spk = CCPubKey(cc.get(), ccSubVer); + char ccaddress[KOMODO_ADDRESS_BUFSIZE]; + Getscriptaddress(ccaddress, spk); + result.push_back(ccaddress); + } + } + else { + CScript spk = CCPubKey(cc.get(), CC_OLD_V1_SUBVER); + char ccaddress[KOMODO_ADDRESS_BUFSIZE]; + Getscriptaddress(ccaddress, spk); + result.push_back(ccaddress); + } + return result; +} + +extern bool fAddressIndex; +// search for pubkey for a given normal address +UniValue searchforpubkey(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || params.size() != 1) + { + string msg = "searchforpubkey R-address\n" + "\nSearch for the pubkey for an address by trying to get a spending input for an utxo on this address. AddressIndex is required\n" + "\nArguments:\n" + "address\n\n"; + throw std::runtime_error(msg); + } + if (!fAddressIndex) throw std::runtime_error("address index not enabled"); + std::string address = params[0].get_str(); + const bool NORMAL_OUTPUTS = false; + + std::vector> addressIndex; + SetAddressIndexOutputs(addressIndex, (char*)address.c_str(), NORMAL_OUTPUTS); + for (std::vector>::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) { + if (it->first.spending) { + CTransaction tx; + uint256 blockHash; + if (myGetTransaction(it->first.txhash, tx, blockHash)) { + if (it->first.index < tx.vin.size()) { + vuint8_t pk, sig; + if (E_UNMARSHAL(vuint8_t(tx.vin[it->first.index].scriptSig.begin(), tx.vin[it->first.index].scriptSig.end()), ss >> sig; ss >> pk)) + return HexStr(pk); + } + } + } + } + if (addressIndex.size() == 0) + throw std::runtime_error("address not found"); + else + throw std::runtime_error("pubkey not found"); +} + +UniValue faucetaddccinputs(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || params.size() > 1) + { + string msg = "faucetaddccinputs [amount]\n" + "\nReturns a new tx with added normal inputs and previous txns. Note that the caller must add the change output\n" + "\nArguments:\n" + //"address which utxos are added from\n" + "amount is deprecated and set to FAUCETSIZE\n" + "Result: json object with created tx and added vin txns\n\n"; + throw runtime_error(msg); + } + CAmount amount = FAUCETSIZE; + + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + std::vector vintxns; + struct CCcontract_info *cp, C; + cp = CCinit(&C, EVAL_FAUCET); + CPubKey faucetpk = GetUnspendable(cp,0); + + CAmount added = AddFaucetInputs(cp, mtx, faucetpk, amount, CC_MAXVINS); + if (added < amount) + throw runtime_error("could not find normal inputs"); + + for (auto const & vin : mtx.vin) { + CTransaction tx; + uint256 hashBlock; + if (myGetTransaction(vin.prevout.hash, tx, hashBlock)) + vintxns.push_back(tx); + } + + UniValue result (UniValue::VOBJ); + UniValue array (UniValue::VARR); + + result.pushKV("txhex", HexStr(E_MARSHAL(ss << mtx))); + for (auto const &vtx : vintxns) + array.push_back(HexStr(E_MARSHAL(ss << vtx))); + result.pushKV("previousTxns", array); + return result; +} static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // -------------- ------------------------ ----------------------- ---------- - // tokens & assets - { "ccutils", "listccunspents", &listccunspents, true } + // cc helpers + { "ccutils", "listccunspents", &listccunspents, true }, + { "ccutils", "getindexkeyforcc", &getindexkeyforcc, true }, + { "ccutils", "searchforpubkey", &searchforpubkey, true }, + { "nspv", "createtxwithnormalinputs", &createtxwithnormalinputs, true }, + { "nspv", "gettransactionsmany", &gettransactionsmany, true }, + { "nspv", "faucetaddccinputs", &faucetaddccinputs, true }, + }; void RegisterCCUtilsRPCCommands(CRPCTable &tableRPC) diff --git a/src/rpc/crosschain.cpp b/src/rpc/crosschain.cpp index f1e8fcad1a6..617c54b0b13 100644 --- a/src/rpc/crosschain.cpp +++ b/src/rpc/crosschain.cpp @@ -49,7 +49,7 @@ using namespace std; extern std::string ASSETCHAINS_SELFIMPORT; extern uint16_t ASSETCHAINS_CODAPORT, ASSETCHAINS_BEAMPORT; -int32_t ensure_CCrequirements(uint8_t evalcode); +//int32_t ensure_CCrequirements(uint8_t evalcode); bool EnsureWalletIsAvailable(bool avoidException); @@ -363,10 +363,6 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp, const if (vopretNonfungible.empty()) throw runtime_error("No non-fungible token data\n"); */ - uint8_t destEvalCode = EVAL_TOKENS; - if (!vopretNonfungible.empty()) - destEvalCode = vopretNonfungible.begin()[0]; - // check non-fungible tokens amount if (!vopretNonfungible.empty() && burnAmount != 1) throw JSONRPCError(RPC_TYPE_ERROR, "For non-fungible tokens amount should be equal to 1."); @@ -386,7 +382,7 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp, const // make payouts (which will be in the import tx with token): 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)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS, burnAmount, destPubKey)); std::vector opretsnft; if (!vopretNonfungible.empty()) @@ -401,18 +397,18 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp, const mtx.vout.clear(); // remove payouts // now make burn transaction: - mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, burnAmount, pubkey2pk(ParseHex(CC_BURNPUBKEY)))); // burn tokens + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS, burnAmount, pubkey2pk(ParseHex(CC_BURNPUBKEY_FIXED)))); // burn tokens int64_t change = inputs - txfee; if (change != 0) mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(myPubKey)) << OP_CHECKSIG)); // make change here to prevent it from making in FinalizeCCtx std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY))); // maybe we do not need this because ccTokens has the const for burn pubkey + voutTokenPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY_FIXED))); // maybe we do not need this because ccTokens has the const for burn pubkey int64_t ccChange = ccInputs - burnAmount; if (ccChange != 0) - mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, ccChange, myPubKey)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS, ccChange, myPubKey)); GetOpReturnData(burnOut.scriptPubKey, vopretBurnData); mtx.vout.push_back(CTxOut(txfee, EncodeTokenOpRetV1(tokenid, voutTokenPubkeys, { vopretBurnData }))); //burn txfee for miners in dest chain @@ -1413,16 +1409,10 @@ UniValue getwalletburntransactions(const UniValue& params, bool fHelp, const CPu if (tokenbasetx.vout.size() > 0 && DecodeTokenCreateOpRetV1(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); - if (!vopretNonfungible.empty()) - destEvalCode = vopretNonfungible.begin()[0]; - int64_t burnAmount = 0; for (auto v : pwtx->vout) if (v.scriptPubKey.IsPayToCryptoCondition() && - CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(destEvalCode ? destEvalCode : EVAL_TOKENS, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY)))) // burned to dead pubkey + CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(EVAL_TOKENS, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY_FIXED)))) // burned to dead pubkey burnAmount += v.nValue; entry.push_back(Pair("burnedAmount", ValueFromAmount(burnAmount))); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index d2e891556c2..bb0e475fd06 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -50,12 +50,6 @@ using namespace std; #include "komodo_defs.h" -extern int32_t ASSETCHAINS_FOUNDERS; -uint64_t komodo_commission(const CBlock *pblock,int32_t height); -int32_t komodo_blockload(CBlock& block,CBlockIndex *pindex); -arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc,int32_t newStakerActive); -int32_t komodo_newStakerActive(int32_t height, uint32_t timestamp); - /** * Return average network hashes per second based on the last 'lookup' blocks, * or over the difficulty averaging window if 'lookup' is nonpositive. @@ -404,7 +398,6 @@ UniValue setgenerate(const UniValue& params, bool fHelp, const CPubKey& mypk) } #endif -CBlockIndex *komodo_chainactive(int32_t height); arith_uint256 zawy_ctB(arith_uint256 bnTarget,uint32_t solvetime); UniValue genminingCSV(const UniValue& params, bool fHelp, const CPubKey& mypk) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 56b3a515b77..d5e9f06d6a1 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -28,12 +28,14 @@ #include "txmempool.h" #include "util.h" #include "notaries_staked.h" +#include "komodo_defs.h" #include "cc/eval.h" #include "cc/CCinclude.h" #ifdef ENABLE_WALLET #include "wallet/wallet.h" #include "wallet/walletdb.h" #endif +#include "komodo_version.h" #include @@ -59,33 +61,9 @@ using namespace std; * Or alternatively, create a specific query method for the information. **/ -int32_t Jumblr_depositaddradd(char *depositaddr); -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); -//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; -extern int32_t KOMODO_LASTMINED,JUMBLR_PAUSE,KOMODO_LONGESTCHAIN,IS_STAKED_NOTARY,IS_KOMODO_NOTARY,STAKED_ERA,KOMODO_INSYNC; -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -uint32_t komodo_segid32(char *coinaddr); -int64_t komodo_coinsupply(int64_t *zfundsp,int64_t *sproutfundsp,int32_t height); -int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *kmdnotarized_heightp); -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.6.1" +// #define KOMODO_VERSION "0.6.1" // see komodo_version.h #define VERUS_VERSION "0.4.0g" -extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; -extern uint32_t ASSETCHAINS_CC; -extern uint32_t ASSETCHAINS_MAGIC,ASSETCHAINS_ALGO; -extern uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY; -extern int32_t ASSETCHAINS_LWMAPOS,ASSETCHAINS_SAPLING,ASSETCHAINS_STAKED; -extern uint64_t ASSETCHAINS_ENDSUBSIDY[],ASSETCHAINS_REWARD[],ASSETCHAINS_HALVING[],ASSETCHAINS_DECAY[],ASSETCHAINS_NOTARY_PAY[]; -extern std::string NOTARY_PUBKEY,NOTARY_ADDRESS; extern uint8_t NOTARY_PUBKEY33[]; +// see also TOKEL_VERSION in komodo_version.h int32_t getera(int timestamp) { @@ -221,11 +199,11 @@ UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) + HelpExampleCli("getinfo", "") + HelpExampleRpc("getinfo", "") ); - //#ifdef ENABLE_WALLET - // LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); - //#else - LOCK(cs_main); - //#endif + #ifdef ENABLE_WALLET + LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); + #else + LOCK(cs_main); + #endif proxyType proxy; GetProxy(NET_IPV4, proxy); @@ -235,7 +213,8 @@ UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) UniValue obj(UniValue::VOBJ); obj.push_back(Pair("version", CLIENT_VERSION)); obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); - obj.push_back(Pair("KMDversion", KOMODO_VERSION)); + obj.push_back(Pair("KMDversion", FormatVersion(KOMODO_VERSION))); + obj.push_back(Pair("TokelVersion", FormatVersion(TOKEL_VERSION))); obj.push_back(Pair("synced", KOMODO_INSYNC!=0)); //obj.push_back(Pair("VRSCversion", VERUS_VERSION)); obj.push_back(Pair("notarized", notarized_height)); @@ -413,6 +392,15 @@ class DescribeAddressVisitor : public boost::static_visitor } return obj; } + + UniValue operator()(const CCLTVID &cltvID) const { + if (cltvID.which() == TX_PUBKEY) + return this->operator()(cltvID.GetPubKey()); + else + return this->operator()(cltvID.GetKeyID()); + } + + UniValue operator()(const CCryptoConditionID &ccID) const { return UniValue(UniValue::VOBJ); } // cryptoconditions are not recognised in the wallet yet }; #endif @@ -880,7 +868,7 @@ bool getAddressFromIndex(const int &type, const uint160 &hash, std::string &addr } else if (type == 1) { address = CBitcoinAddress(CKeyID(hash)).ToString(); } else if (type == 3) { - address = CBitcoinAddress(CKeyID(hash)).ToString(); + address = CBitcoinAddress(CCryptoConditionID(hash)).ToString(); } else { return false; } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index f87d953eaf1..70463f4a232 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -28,6 +28,7 @@ #include "util.h" #include "version.h" #include "deprecation.h" +#include "komodo_version.h" #include @@ -171,69 +172,6 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) return ret; } -int32_t KOMODO_LONGESTCHAIN; -int32_t komodo_longestchain() -{ - static int32_t depth; - int32_t ht,n=0,num=0,maxheight=0,height = 0; - if ( depth < 0 ) - depth = 0; - if ( depth == 0 ) - { - - /** - * Seems here we need to try to lock cs_main, to avoid wrong order of lock (cs_main, cs_vNodes), - * implementation of getting max(nStartingHeight, nSyncHeight, nCommonHeight) from CNodeStateStats - * and loop here is similar to getpeerinfo RPC and there we have LOCK(cs_main). If we'll not able - * to acquire lock on cs_main komodo_longestchain() will return previous saved value of - * KOMODO_LONGESTCHAIN, anyway, on next call it will be updated, when lock will success. - */ - - TRY_LOCK(cs_main, lockMain); // Acquire cs_main - if (!lockMain) { - return(KOMODO_LONGESTCHAIN); - } - - depth++; - vector vstats; - { - //LOCK(cs_main); - CopyNodeStats(vstats); - } - BOOST_FOREACH(const CNodeStats& stats, vstats) - { - //fprintf(stderr,"komodo_longestchain iter.%d\n",n); - CNodeStateStats statestats; - bool fStateStats = GetNodeStateStats(stats.nodeid,statestats); - if ( statestats.nSyncHeight < 0 ) - continue; - ht = 0; - if ( stats.nStartingHeight > ht ) - ht = stats.nStartingHeight; - if ( statestats.nSyncHeight > ht ) - ht = statestats.nSyncHeight; - if ( statestats.nCommonHeight > ht ) - ht = statestats.nCommonHeight; - if ( maxheight == 0 || ht > maxheight*1.01 ) - maxheight = ht, num = 1; - else if ( ht > maxheight*0.99 ) - num++; - if ( ht > height ) - height = ht; - } - depth--; - if ( num > (n >> 1) ) - { - if ( 0 && height != KOMODO_LONGESTCHAIN ) - fprintf(stderr,"set %s KOMODO_LONGESTCHAIN <- %d\n",ASSETCHAINS_SYMBOL,height); - KOMODO_LONGESTCHAIN = height; - return(height); - } - KOMODO_LONGESTCHAIN = 0; - } - return(KOMODO_LONGESTCHAIN); -} - UniValue addnode(const UniValue& params, bool fHelp, const CPubKey& mypk) { string strCommand; @@ -489,7 +427,7 @@ UniValue getdeprecationinfo(const UniValue& params, bool fHelp, const CPubKey& m UniValue obj(UniValue::VOBJ); obj.push_back(Pair("version", CLIENT_VERSION)); obj.push_back(Pair("subversion", - FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector()))); + FormatSubVersion(CLIENT_NAME, KOMODO_VERSION, { FormatVersion(TOKEL_VERSION) }))); obj.push_back(Pair("deprecationheight", DEPRECATION_HEIGHT)); return obj; diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index eaf1ec002c5..0413dc2974e 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -41,20 +41,14 @@ #endif #include "komodo_defs.h" +#include "cc/CCinclude.h" #include - #include - #include -int32_t komodo_notarized_height(int32_t *prevMoMheightp,uint256 *hashp,uint256 *txidp); - using namespace std; -extern char ASSETCHAINS_SYMBOL[]; -int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); - void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) { txnouttype type; @@ -79,6 +73,16 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fInclud a.push_back(EncodeDestination(addr)); } out.push_back(Pair("addresses", a)); + + // add cc decoded + if (scriptPubKey.IsPayToCCV2()) { + std::vector ccdata = scriptPubKey.GetCCV2SPK(); + CC* cond = cc_readConditionBinaryMaybeMixed(ccdata.data(), ccdata.size()); + if (cond) { + out.push_back(Pair("condition", CCDecodeMixedMode(cond))); + cc_free(cond); + } + } } UniValue TxJoinSplitToJSON(const CTransaction& tx) { @@ -140,8 +144,6 @@ UniValue TxJoinSplitToJSON(const CTransaction& tx) { return vjoinsplit; } -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); - UniValue TxShieldedSpendsToJSON(const CTransaction& tx) { UniValue vdesc(UniValue::VARR); for (const SpendDescription& spendDesc : tx.vShieldedSpend) { diff --git a/src/rpc/register.h b/src/rpc/register.h index 829143591a5..aebb427327e 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -38,7 +38,10 @@ void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC); void RegisterTokensRPCCommands(CRPCTable &tableRPC); // cc utils rpcs: void RegisterCCUtilsRPCCommands(CRPCTable &tableRPC); - +#ifdef ENABLE_WEBSOCKETS +// websockets helper rpcs +void RegisterWebSocketsRPCCommands(CRPCTable &tableRPC); +#endif static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) { @@ -49,6 +52,9 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) RegisterRawTransactionRPCCommands(tableRPC); RegisterTokensRPCCommands(tableRPC); RegisterCCUtilsRPCCommands(tableRPC); +#ifdef ENABLE_WEBSOCKETS + RegisterWebSocketsRPCCommands(tableRPC); +#endif } #endif diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 57068503a5b..35e84591d72 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -417,8 +417,8 @@ static const CRPCCommand vRPCCommands[] = { "FSM", "FSMlist", &FSMlist, true }, { "FSM", "FSMinfo", &FSMinfo, true }, - // DEX - { "DEX", "DEX_broadcast", &DEX_broadcast, true }, + // DEX (disabled) +/* { "DEX", "DEX_broadcast", &DEX_broadcast, true }, { "DEX", "DEX_anonsend", &DEX_anonsend, true }, { "DEX", "DEX_list", &DEX_list, true }, { "DEX", "DEX_get", &DEX_get, true }, @@ -430,7 +430,7 @@ static const CRPCCommand vRPCCommands[] = { "DEX", "DEX_subscribe", &DEX_subscribe, true }, { "DEX", "DEX_stream", &DEX_stream, true }, { "DEX", "DEX_streamsub", &DEX_streamsub, true }, - { "DEX", "DEX_notarize", &DEX_notarize, true }, + { "DEX", "DEX_notarize", &DEX_notarize, true }, */ // fsm { "nSPV", "nspv_getinfo", &nspv_getinfo, true }, @@ -876,7 +876,11 @@ std::string HelpExampleCli(const std::string& methodname, const std::string& arg } else if ((strncmp(ASSETCHAINS_SYMBOL, "HUSH3", 5) == 0) ) { return "> hush-cli " + methodname + " " + args + "\n"; } else { - return "> komodo-cli -ac_name=" + strprintf("%s", ASSETCHAINS_SYMBOL) + " " + methodname + " " + args + "\n"; +#if !defined CUSTOM_BIN_NAME + return std::string("> komodo-cli -ac_name=") + std::string(ASSETCHAINS_SYMBOL) + " " + methodname + " " + args + "\n"; +#else + return std::string("> ") + std::string(CUSTOM_BIN_NAME) + std::string("-cli") + " " + methodname + " " + args + "\n"; +#endif } } diff --git a/src/rpc/server.h b/src/rpc/server.h index 5975e0e1d8b..f8e27b156fb 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -35,6 +35,9 @@ #include #include +#include "primitives/block.h" +#include "primitives/transaction.h" +#include "script/script.h" class AsyncRPCQueue; class CRPCCommand; @@ -180,355 +183,363 @@ extern CRPCTable tableRPC; * Utilities: convert hex-encoded Values * (throws error if not hex). */ -extern uint256 ParseHashV(const UniValue& v, std::string strName); -extern uint256 ParseHashO(const UniValue& o, std::string strKey); -extern std::vector ParseHexV(const UniValue& v, std::string strName); -extern std::vector ParseHexO(const UniValue& o, std::string strKey); +uint256 ParseHashV(const UniValue& v, std::string strName); +uint256 ParseHashO(const UniValue& o, std::string strKey); +std::vector ParseHexV(const UniValue& v, std::string strName); +std::vector ParseHexO(const UniValue& o, std::string strKey); extern int64_t nWalletUnlockTime; -extern CAmount AmountFromValue(const UniValue& value); -extern UniValue ValueFromAmount(const CAmount& amount); -extern double GetDifficulty(const CBlockIndex* blockindex = NULL); -extern double GetNetworkDifficulty(const CBlockIndex* blockindex = NULL); -extern std::string HelpRequiringPassphrase(); -extern std::string HelpExampleCli(const std::string& methodname, const std::string& args); -extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args); - -extern void EnsureWalletIsUnlocked(); +CAmount AmountFromValue(const UniValue& value); +UniValue ValueFromAmount(const CAmount& amount); +double GetDifficulty(const CBlockIndex* blockindex = NULL); +double GetNetworkDifficulty(const CBlockIndex* blockindex = NULL); +std::string HelpRequiringPassphrase(); +std::string HelpExampleCli(const std::string& methodname, const std::string& args); +std::string HelpExampleRpc(const std::string& methodname, const std::string& args); + +void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); +UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false); +UniValue mempoolInfoToJSON(); +UniValue mempoolToJSON(bool fVerbose = false); +void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); +UniValue blockheaderToJSON(const CBlockIndex* blockindex); +UniValue TxJoinSplitToJSON(const CTransaction& tx); + +void EnsureWalletIsUnlocked(); bool StartRPC(); void InterruptRPC(); void StopRPC(); std::string JSONRPCExecBatch(const UniValue& vReq); -extern std::string experimentalDisabledHelpMsg(const std::string& rpc, const std::string& enableArg); - -extern UniValue getconnectioncount(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcnet.cpp -extern UniValue getaddressmempool(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getaddressutxos(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getaddressdeltas(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getaddresstxids(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getsnapshot(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getaddressbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getpeerinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue checknotarization(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getnotarypayinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue ping(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue addnode(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue disconnectnode(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getaddednodeinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getnettotals(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue setban(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue listbanned(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue clearbanned(const UniValue& params, bool fHelp, const CPubKey& mypk); - -extern UniValue dumpprivkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp -extern UniValue importprivkey(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue importaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue dumpwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue importwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); - -extern UniValue getgenerate(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcmining.cpp -extern UniValue setgenerate(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue generate(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getlocalsolps(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getnetworksolps(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getnetworkhashps(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getmininginfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue prioritisetransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getblocktemplate(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue submitblock(const UniValue& params, bool fHelp, const CPubKey& mypk); -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 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); -extern UniValue heirclaim(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue heirinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue heirlist(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue channelsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue oraclesaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue oracleslist(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue oraclesinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue oraclescreate(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue oraclesfund(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue oraclesregister(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue oraclessubscribe(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue oraclesdata(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue oraclessample(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue oraclessamples(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pricesaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue priceslist(const UniValue& params, bool fHelp, const CPubKey& mypk); -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 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); -extern UniValue payments_merge(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue payments_txidopret(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue payments_create(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue payments_airdrop(const UniValue& params, bool fHelp, const CPubKey& mypk); -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); -extern UniValue gatewaysaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue gatewayslist(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue gatewaysinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue gatewaysdumpprivkey(const UniValue& params, bool fHelp, const CPubKey& mypk); -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 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 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 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); -//extern UniValue tokenfillswap(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue faucetfund(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue faucetget(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue faucetaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue faucetinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue rewardsinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue rewardslist(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue rewardsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue rewardscreatefunding(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue rewardsaddfunding(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue rewardslock(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue rewardsunlock(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue diceaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue dicefund(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue dicelist(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue diceinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue diceaddfunds(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue dicebet(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue dicefinish(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue dicestatus(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue lottoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue FSMaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue FSMcreate(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue FSMlist(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue FSMinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue auctionaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pegscreate(const UniValue& params, bool fHelp, const CPubKey& mypk); -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 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); -extern UniValue pegsaccountinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pegsworstaccounts(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pegsinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); - -extern UniValue getnewaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -//extern UniValue getnewaddress64(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue getaccountaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getrawchangeaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue setaccount(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getaccount(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getaddressesbyaccount(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue sendtoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue signmessage(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue verifymessage(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getreceivedbyaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getreceivedbyaccount(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue cleanwallettransactions(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getbalance64(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getunconfirmedbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue movecmd(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue sendfrom(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue addmultisigaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue createmultisig(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue listreceivedbyaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue listreceivedbyaccount(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue listtransactions(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue listaddressgroupings(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue listaccounts(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue listsinceblock(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue gettransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue backupwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue keypoolrefill(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue walletpassphrase(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue walletpassphrasechange(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue walletlock(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue encryptwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue txnotarizedconfirmed(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue decodeccopret(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getiguanajson(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getnotarysendmany(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue geterablockheights(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue setpubkey(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue setstakingsplit(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getwalletinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getblockchaininfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getnetworkinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getdeprecationinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue setmocktime(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue resendwallettransactions(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue zc_benchmark(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue zc_raw_keygen(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue zc_raw_receive(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue zc_sample_joinsplit(const UniValue& params, bool fHelp, const CPubKey& mypk); - -extern UniValue jumblr_deposit(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue jumblr_secret(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue jumblr_pause(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue jumblr_resume(const UniValue& params, bool fHelp, const CPubKey& mypk); - -extern UniValue getrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rcprawtransaction.cpp -extern UniValue listunspent(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue lockunspent(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue listlockunspent(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue createrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue decoderawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue decodescript(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue fundrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue signrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue sendrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue gettxoutproof(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue verifytxoutproof(const UniValue& params, bool fHelp, const CPubKey& mypk); - -extern UniValue getblockcount(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcblockchain.cpp -extern UniValue getbestblockhash(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getdifficulty(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue settxfee(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getmempoolinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getrawmempool(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getblockhashes(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getblockdeltas(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getblockhash(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getblockheader(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getlastsegidstakes(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getblock(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue gettxoutsetinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue gettxout(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue verifychain(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getchaintips(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue invalidateblock(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue reconsiderblock(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getspentinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue selfimport(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue importdual(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue importgatewayaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue importgatewayinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -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 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 genminingCSV(const UniValue& params, bool fHelp, const CPubKey& mypk); - -extern UniValue nspv_getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue nspv_login(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue nspv_listtransactions(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue nspv_mempool(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue nspv_listunspent(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue nspv_spentinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue nspv_notarizations(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue nspv_hdrsproof(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue nspv_txproof(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue nspv_spend(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue nspv_broadcast(const UniValue& params, bool fHelp, const CPubKey& mypk); -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 -extern UniValue z_importkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp -extern UniValue z_exportviewingkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp -extern UniValue z_importviewingkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp -extern UniValue z_getnewaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue z_listaddresses(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue z_exportwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp -extern UniValue z_importwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp -extern UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue z_getbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue z_gettotalbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue z_shieldcoinbase(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue z_getoperationstatus(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue z_getoperationresult(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue z_listoperationids(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue opreturn_burn(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp -extern UniValue z_validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcmisc.cpp -extern UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdisclosure.cpp -extern UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdisclosure.cpp - -extern UniValue MoMoMdata(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue calc_MoM(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue height_MoM(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue assetchainproof(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue crosschainproof(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue scanNotarisationsDB(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getimports(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue getwalletburntransactions(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue migrate_converttoexport(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue migrate_createburntransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue migrate_checkburntransactionsource(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue migrate_createnotaryapprovaltransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); - -extern UniValue notaries(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue minerids(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue kvsearch(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue kvupdate(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue paxprice(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue paxpending(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue paxprices(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue paxdeposit(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue paxwithdraw(const UniValue& params, bool fHelp, const CPubKey& mypk); - -extern UniValue prices(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pricesbet(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pricessetcostbasis(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pricescashout(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pricesrekt(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pricesaddfunding(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pricesgetorderbook(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue pricesrefillfund(const UniValue& params, bool fHelp, const CPubKey& mypk); +std::string experimentalDisabledHelpMsg(const std::string& rpc, const std::string& enableArg); + +UniValue getconnectioncount(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcnet.cpp +UniValue getaddressmempool(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getaddressutxos(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getaddressdeltas(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getaddresstxids(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getsnapshot(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getaddressbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getpeerinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue checknotarization(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getnotarypayinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue ping(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue addnode(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue disconnectnode(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getaddednodeinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getnettotals(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue setban(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue listbanned(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue clearbanned(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue dumpprivkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp +UniValue importprivkey(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue dumpwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue getgenerate(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcmining.cpp +UniValue setgenerate(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue generate(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getlocalsolps(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getnetworksolps(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getnetworkhashps(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getmininginfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue prioritisetransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getblocktemplate(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue submitblock(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue estimatefee(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue estimatepriority(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue coinsupply(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue heiraddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue heirfund(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue heiradd(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue heirclaim(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue heirinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue heirlist(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue channelsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue oraclesaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue oracleslist(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue oraclesinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue oraclescreate(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue oraclesfund(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue oraclesregister(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue oraclessubscribe(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue oraclesdata(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue oraclessample(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue oraclessamples(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pricesaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue priceslist(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue mypriceslist(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pricesinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pegsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue paymentsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue payments_release(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue payments_fund(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue payments_merge(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue payments_txidopret(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue payments_create(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue payments_airdrop(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue payments_airdroptokens(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue payments_info(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue payments_list(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue cclibaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue cclibinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue cclib(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewaysaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewayslist(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewaysinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewaysdumpprivkey(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewaysexternaladdress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewaysbind(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewaysdeposit(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewaysclaim(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewayswithdraw(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewayspartialsign(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewayscompletesigning(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewaysmarkdone(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewayspendingdeposits(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewayspendingwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gatewaysprocessed(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue channelslist(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue channelsinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue channelsopen(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue channelspayment(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue channelsclose(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue channelsrefund(const UniValue& params, bool fHelp, const CPubKey& mypk); +//UniValue tokenswapask(const UniValue& params, bool fHelp, const CPubKey& mypk); +//UniValue tokenfillswap(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue faucetfund(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue faucetget(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue faucetaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue faucetinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue rewardsinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue rewardslist(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue rewardsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue rewardscreatefunding(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue rewardsaddfunding(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue rewardslock(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue rewardsunlock(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue diceaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue dicefund(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue dicelist(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue diceinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue diceaddfunds(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue dicebet(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue dicefinish(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue dicestatus(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue lottoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue FSMaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue FSMcreate(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue FSMlist(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue FSMinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue auctionaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pegscreate(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pegsfund(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pegsget(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pegsredeem(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pegsliquidate(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pegsexchange(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pegsaccounthistory(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pegsaccountinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pegsworstaccounts(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pegsinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue getnewaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +//UniValue getnewaddress64(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue getaccountaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getrawchangeaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue setaccount(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getaccount(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getaddressesbyaccount(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue sendtoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue signmessage(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue verifymessage(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getreceivedbyaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getreceivedbyaccount(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue cleanwallettransactions(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getbalance64(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getunconfirmedbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue movecmd(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue sendfrom(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue addmultisigaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue createmultisig(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue listreceivedbyaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue listreceivedbyaccount(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue listtransactions(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue listaddressgroupings(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue listaccounts(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue listsinceblock(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gettransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue backupwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue keypoolrefill(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue walletpassphrase(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue walletpassphrasechange(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue walletlock(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue encryptwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue txnotarizedconfirmed(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue decodeccopret(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getiguanajson(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getnotarysendmany(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue geterablockheights(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue setpubkey(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue setstakingsplit(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getwalletinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getblockchaininfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getnetworkinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getdeprecationinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue setmocktime(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue resendwallettransactions(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue zc_benchmark(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue zc_raw_keygen(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue zc_raw_receive(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue zc_sample_joinsplit(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue jumblr_deposit(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue jumblr_secret(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue jumblr_pause(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue jumblr_resume(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue getrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rcprawtransaction.cpp +UniValue listunspent(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue lockunspent(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue listlockunspent(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue createrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue decoderawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue decodescript(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue fundrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue signrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue sendrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gettxoutproof(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue verifytxoutproof(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue getblockcount(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcblockchain.cpp +UniValue getbestblockhash(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getdifficulty(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue settxfee(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getmempoolinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getrawmempool(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getblockhashes(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getblockdeltas(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getblockhash(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getblockheader(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getlastsegidstakes(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getblock(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gettxoutsetinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue gettxout(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue verifychain(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getchaintips(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue invalidateblock(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue reconsiderblock(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getspentinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue selfimport(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importdual(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importgatewayaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importgatewayinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importgatewaybind(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importgatewaydeposit(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importgatewaywithdraw(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importgatewaywithdrawsign(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importgatewaymarkdone(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importgatewaypendingsignwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importgatewaysignedwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importgatewayexternaladdress(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue importgatewaydumpprivkey(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue genminingCSV(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue nspv_getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_login(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_listtransactions(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_mempool(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_listunspent(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_spentinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_notarizations(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_hdrsproof(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_txproof(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_spend(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_broadcast(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_logout(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue nspv_listccmoduleunspent(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue DEX_broadcast(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_anonsend(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_list(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_get(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_stats(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_orderbook(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_cancel(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_setpubkey(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_publish(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_subscribe(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_stream(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_streamsub(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue DEX_notarize(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue getblocksubsidy(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue z_exportkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp +UniValue z_importkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp +UniValue z_exportviewingkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp +UniValue z_importviewingkey(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp +UniValue z_getnewaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue z_listaddresses(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue z_exportwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp +UniValue z_importwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdump.cpp +UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue z_getbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue z_gettotalbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue z_shieldcoinbase(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue z_getoperationstatus(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue z_getoperationresult(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue z_listoperationids(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue opreturn_burn(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +UniValue z_validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcmisc.cpp +UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdisclosure.cpp +UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdisclosure.cpp + +UniValue MoMoMdata(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue calc_MoM(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue height_MoM(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue assetchainproof(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue crosschainproof(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue scanNotarisationsDB(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getimports(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue getwalletburntransactions(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue migrate_converttoexport(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue migrate_createburntransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue migrate_checkburntransactionsource(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue migrate_createnotaryapprovaltransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue notaries(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue minerids(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue kvsearch(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue kvupdate(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue paxprice(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue paxpending(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue paxprices(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue paxdeposit(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue paxwithdraw(const UniValue& params, bool fHelp, const CPubKey& mypk); + +UniValue prices(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pricesbet(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pricessetcostbasis(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pricescashout(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pricesrekt(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pricesaddfunding(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pricesgetorderbook(const UniValue& params, bool fHelp, const CPubKey& mypk); +UniValue pricesrefillfund(const UniValue& params, bool fHelp, const CPubKey& mypk); diff --git a/src/rpc/testtransactions.cpp b/src/rpc/testtransactions.cpp deleted file mode 100644 index c41e97e1036..00000000000 --- a/src/rpc/testtransactions.cpp +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-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. - -/****************************************************************************** - * 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 "amount.h" -#include "chain.h" -#include "chainparams.h" -#include "checkpoints.h" -#include "crosschain.h" -#include "base58.h" -#include "consensus/validation.h" -#include "cc/eval.h" -#include "main.h" -#include "primitives/transaction.h" -#include "rpc/server.h" -#include "streams.h" -#include "sync.h" -#include "util.h" -#include "script/script.h" -#include "script/script_error.h" -#include "script/sign.h" -#include "script/standard.h" - -#include - -#include - -#include - - -#include "cc/CCinclude.h" -#include "cc/CCPrices.h" - -using namespace std; - -int32_t ensure_CCrequirements(uint8_t evalcode); - -UniValue test_ac(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 4)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_HEIR) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - std::vector pubkey1; - std::vector pubkey2; - - pubkey1 = ParseHex(params[0].get_str().c_str()); - pubkey2 = ParseHex(params[1].get_str().c_str()); - - CPubKey pk1 = pubkey2pk(pubkey1); - CPubKey pk2 = pubkey2pk(pubkey2); - - if (!pk1.IsValid() || !pk2.IsValid()) - throw runtime_error("invalid pubkey\n"); - - int64_t txfee = 10000; - int64_t amount = atoll(params[2].get_str().c_str()) * COIN; - uint256 fundingtxid = Parseuint256((char *)params[3].get_str().c_str()); - - CPubKey myPubkey = pubkey2pk(Mypubkey()); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, txfee + amount, 60); - - if (normalInputs < txfee + amount) - throw runtime_error("not enough normals\n"); - - mtx.vout.push_back(MakeCC1of2vout(EVAL_HEIR, amount, pk1, pk2)); - - CScript opret; - fundingtxid = revuint256(fundingtxid); - - opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'A' << fundingtxid << (uint8_t)0); - - cp = CCinit(&C, EVAL_HEIR); - return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, opret)); -} - -UniValue test_heirmarker(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 1)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_HEIR) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - uint256 fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - - CPubKey myPubkey = pubkey2pk(Mypubkey()); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); - if (normalInputs < 10000) - throw runtime_error("not enough normals\n"); - - mtx.vin.push_back(CTxIn(fundingtxid, 1)); - mtx.vout.push_back(MakeCC1vout(EVAL_HEIR, 10000, myPubkey)); - - CScript opret; - fundingtxid = revuint256(fundingtxid); - - opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'C' << fundingtxid << (uint8_t)0); - - cp = CCinit(&C, EVAL_HEIR); - return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); -} - -UniValue test_burntx(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 1)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); - - CPubKey myPubkey = pubkey2pk(Mypubkey()); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); - if (normalInputs < 10000) - throw runtime_error("not enough normals\n"); - - CPubKey burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); - - mtx.vin.push_back(CTxIn(tokenid, 0)); - mtx.vin.push_back(CTxIn(tokenid, 1)); - mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS, 1, burnpk)); - - std::vector voutPubkeys; - voutPubkeys.push_back(burnpk); - - cp = CCinit(&C, EVAL_TOKENS); - - std::vector vopret; - GetNonfungibleData(tokenid, vopret); - if (vopret.size() > 0) - cp->additionalTokensEvalcode2 = vopret.begin()[0]; - - uint8_t tokenpriv[33]; - char unspendableTokenAddr[64]; - CPubKey unspPk = GetUnspendable(cp, tokenpriv); - GetCCaddress(cp, unspendableTokenAddr, unspPk); - CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); - return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, EncodeTokenOpRet(tokenid, voutPubkeys, std::make_pair(0, vscript_t())))); -} - -UniValue test_proof(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); - std::vectorproof; - - if (fHelp || (params.size() != 2)) - throw runtime_error("incorrect params\n"); - - - proof = ParseHex(params[0].get_str()); - uint256 cointxid = Parseuint256((char *)params[1].get_str().c_str()); - - std::vector txids; - - CMerkleBlock merkleBlock; - if (!E_UNMARSHAL(proof, ss >> merkleBlock)) { - result.push_back(Pair("error", "could not unmarshal proof")); - return result; - } - uint256 merkleRoot = merkleBlock.txn.ExtractMatches(txids); - - result.push_back(Pair("source_root", merkleRoot.GetHex())); - - for (int i = 0; i < txids.size(); i++) - std::cerr << "merkle block txid=" << txids[0].GetHex() << std::endl; - - - std::vector vMatches(txids.size()); - for (auto v : vMatches) v = true; - CPartialMerkleTree verifTree(txids, vMatches); - - result.push_back(Pair("verif_root", verifTree.ExtractMatches(txids).GetHex())); - - if (std::find(txids.begin(), txids.end(), cointxid) == txids.end()) { - fprintf(stderr, "invalid proof for this cointxid\n"); - } - - std::vector vMerkleTree; - bool f; - ::BuildMerkleTree(&f, txids, vMerkleTree); - - std::vector vMerkleBranch = ::GetMerkleBranch(0, txids.size(), vMerkleTree); - - uint256 ourResult = SafeCheckMerkleBranch(zeroid, vMerkleBranch, 0); - result.push_back(Pair("SafeCheckMerkleBranch", ourResult.GetHex())); - - return result; -} - -extern CScript prices_costbasisopret(uint256 bettxid, CPubKey mypk, int32_t height, int64_t costbasis); -UniValue test_pricesmarker(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 1)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_PRICES) < 0) - throw runtime_error(CC_REQUIREMENTS_MSG); - - uint256 bettxid = Parseuint256((char *)params[0].get_str().c_str()); - - cp = CCinit(&C, EVAL_PRICES); - CPubKey myPubkey = pubkey2pk(Mypubkey()); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); - if (normalInputs < 10000) - throw runtime_error("not enough normals\n"); - - mtx.vin.push_back(CTxIn(bettxid, 1)); - mtx.vout.push_back(CTxOut(1000, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG)); - - return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, prices_costbasisopret(bettxid, myPubkey, 100, 100))); -} - - -static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode - // --------------------- ------------------------ ----------------------- ---------- - - /* Not shown in help */ - { "hidden", "test_ac", &test_ac, true }, - { "hidden", "test_heirmarker", &test_heirmarker, true }, - { "hidden", "test_proof", &test_proof, true }, - { "hidden", "test_burntx", &test_burntx, true }, - { "hidden", "test_pricesmarker", &test_pricesmarker, true } -}; - -void RegisterTesttransactionsRPCCommands(CRPCTable &tableRPC) -{ - for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) - tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); -} diff --git a/src/rpc/tokensrpc.cpp b/src/rpc/tokensrpc.cpp index 7dcdaed307d..919715e1bf6 100644 --- a/src/rpc/tokensrpc.cpp +++ b/src/rpc/tokensrpc.cpp @@ -20,6 +20,7 @@ #include "amount.h" #include "rpc/server.h" #include "rpc/protocol.h" +#include "key_io.h" #include "../wallet/crypter.h" #include "../wallet/rpcwallet.h" @@ -35,7 +36,7 @@ using namespace std; -UniValue assetsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) +UniValue assetsindexkey(const UniValue& params, bool fHelp, const CPubKey& mypk) { struct CCcontract_info *cp, C; std::vector pubkey; cp = CCinit(&C, EVAL_ASSETS); @@ -48,7 +49,7 @@ UniValue assetsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) return CCaddress(cp, (char *)"Assets", pubkey); } -UniValue tokenaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) +UniValue tokenindexkey(const UniValue& params, bool fHelp, const CPubKey& mypk) { struct CCcontract_info *cp,C; std::vector pubkey; cp = CCinit(&C, EVAL_TOKENS); @@ -61,34 +62,49 @@ UniValue tokenaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) return CCaddress(cp, "Tokens", pubkey, false); } -UniValue tokenv2address(const UniValue& params, bool fHelp, const CPubKey& mypk) +UniValue tokenv2indexkey(const UniValue& params, bool fHelp, const CPubKey& mypk) { - struct CCcontract_info *cp,C; - vuint8_t pubkey; + if (fHelp || params.size() != 1) + throw runtime_error("tokenv2indexkey pubkey\n" + "returns tokens index keys for pubkey.\n" + "It can be used with getaddressutxos getaddresstxids rpcs to list tokens outputs on this pubkey\n"); + struct CCcontract_info *cp,C; cp = CCinit(&C, EVAL_TOKENSV2); - if (fHelp || params.size() > 1) - throw runtime_error("tokenv2address [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, "Tokensv2", pubkey, true); + vuint8_t vpubkey = ParseHex(params[0].get_str().c_str()); + CPubKey pk = pubkey2pk(vpubkey); + if (!pk.IsValid()) + throw runtime_error("invalid pubkey\n"); + + UniValue result(UniValue::VARR); + std::vector tokenindexkeys = TokensV2::GetTokenIndexKeys(pk); + for (auto const tokenindexkey : tokenindexkeys) + result.push_back(tokenindexkey); + return result; } -UniValue assetsv2address(const UniValue& params, bool fHelp, const CPubKey& mypk) +UniValue assetsv2indexkey(const UniValue& params, bool fHelp, const CPubKey& mypk) { struct CCcontract_info *cp,C; - vuint8_t pubkey; + if (fHelp || params.size() != 1) + throw runtime_error("assetsv2indexkey pubkey\n" + "returns address index key for pubkey.\n" + "It can be used with getaddressutxos getaddresstxids rpcs to list assets cc outputs on this pubkey\n"); cp = CCinit(&C, EVAL_ASSETSV2); - if (fHelp || params.size() > 1) - throw runtime_error("assetsv2address [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, "Assetsv2", pubkey, true); + vuint8_t vpubkey = ParseHex(params[0].get_str().c_str()); + CPubKey pk = pubkey2pk(vpubkey); + if (!pk.IsValid()) + throw runtime_error("invalid pubkey\n"); + + char address[KOMODO_ADDRESS_BUFSIZE]; + GetCCaddress(cp, address, pk, true); + return address; } UniValue tokenlist(const UniValue& params, bool fHelp, const CPubKey& remotepk) @@ -97,32 +113,55 @@ UniValue tokenlist(const UniValue& params, bool fHelp, const CPubKey& remotepk) if (fHelp || params.size() > 0) throw runtime_error("tokenlist\n"); - if (ensure_CCrequirements(EVAL_TOKENS) < 0) + if (ensure_CCrequirements(EVAL_TOKENS, remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); return TokenList(); } UniValue tokenv2list(const UniValue& params, bool fHelp, const CPubKey& remotepk) { - uint256 tokenid; - if (fHelp || params.size() > 0) - throw runtime_error("tokenv2list\n"); + const static std::set acceptable = { "beginHeight", "endHeight", "pubkey", "address" }; - if (ensure_CCrequirements(EVAL_TOKENSV2) < 0) + if (fHelp || params.size() > 1) + throw runtime_error("tokenv2list [json-params]\n" + "json-params optional params as a json object, limiting tokenv2list output:\n" + " { \"beginHeight\": number \"endHeight\": number, \"pubkey\": hexstring, \"address\": string }\n" + " \"beginHeight\", \"endHeight\" - height interval where to search tokenv2create transactions, if beginHeight omitted the first block used, if endHeight omitted the chain tip used" + " \"pubkey\" - search tokens created by a specific pubkey\n" + " \"address\" - search created on a specific cc address\n"); + + if (ensure_CCrequirements(EVAL_TOKENSV2, remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - return TokenV2List();} + UniValue jsonParams; + if (params.size() == 1) + { + if (params[0].getType() == UniValue::VOBJ) + jsonParams = params[0].get_array(); + else if (params[0].getType() == UniValue::VSTR) { // json in quoted string '{...}' + if (!jsonParams.read(params[0].get_str().c_str())) + return MakeResultError("parameter must be a valid json object\n"); + } + if (jsonParams.getType() != UniValue::VOBJ) + throw runtime_error("parameter 1 must be a json object"); + + // check unused params: + for (int i = 0; i < jsonParams.getKeys().size(); i ++) + if (acceptable.count(jsonParams.getKeys()[i]) == 0) + throw runtime_error(std::string("invalid json param") + jsonParams.getKeys()[i]); + } + return TokenV2List(jsonParams); +} template static UniValue tokeninfo(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& remotepk) { - uint256 tokenid; - if ( fHelp || params.size() != 1 ) + if (fHelp || params.size() != 1) throw runtime_error(name + " tokenid\n"); - if ( ensure_CCrequirements(V::EvalCode()) < 0 ) + if (ensure_CCrequirements(V::EvalCode()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - tokenid = Parseuint256((char *)params[0].get_str().c_str()); - return TokenInfo(tokenid); + uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); + return TokenInfo(tokenid, [](const vuint8_t &data){ return NullUniValue; }); } UniValue tokeninfo(const UniValue& params, bool fHelp, const CPubKey& remotepk) @@ -134,35 +173,70 @@ UniValue tokenv2info(const UniValue& params, bool fHelp, const CPubKey& remotepk return tokeninfo("tokenv2info", params, fHelp, remotepk); } +template +static UniValue tokeninfotokel(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || params.size() != 1) + throw runtime_error(name + " tokenid\n"); + if (ensure_CCrequirements(V::EvalCode(), remotepk.IsValid()) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); + return TokenInfo(tokenid, ParseTokelVData); +} + +UniValue tokeninfotokel(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + return tokeninfotokel("tokeninfotokel", params, fHelp, remotepk); +} +UniValue tokenv2infotokel(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + return tokeninfotokel("tokenv2infotokel", params, fHelp, remotepk); +} + template -UniValue tokenorders(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& mypk) +UniValue tokenorders(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& remotepk) { + const static std::set acceptable = { "beginHeight", "endHeight", "pubkey"}; uint256 tokenid; - uint8_t evalcodeNFT = 0; const CPubKey emptypk; - if ( fHelp || params.size() > 2 ) - throw runtime_error(name + " [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(A::EvalCode()) < 0 || ensure_CCrequirements(T::EvalCode()) < 0) + if (fHelp || params.size() > 2) + throw runtime_error(name + " [tokenid|'*'] [json-params]\n" + "returns tokens orders for the tokenid or all available token orders if tokenid is not set\n" + "json-params - optional json object with params limiting orders to output:\n" + " { \"beginHeight\": number \"endHeight\": number, \"pubkey\": hexstring }\n" + " \"beginHeight\", \"endHeight\" - height interval where to search orders, if beginHeight omitted the first block is used, if endHeight omitted the chain tip is used" + " \"pubkey\" - search orders created by a specific pubkey\n\n"); + + if (ensure_CCrequirements(A::EvalCode(), remotepk.IsValid()) < 0 || ensure_CCrequirements(T::EvalCode(), remotepk.IsValid()) < 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) + if (tokenid.IsNull()) 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 + UniValue jsonParams; + if (params.size() >= 2) + { + if (params[1].getType() == UniValue::VOBJ) + jsonParams = params[1].get_array(); + else if (params[1].getType() == UniValue::VSTR) { // json in quoted string '{...}' + if (!jsonParams.read(params[1].get_str().c_str())) + return MakeResultError("parameter 2 must be a valid json object\n"); + } + if (jsonParams.getType() != UniValue::VOBJ) + throw runtime_error("parameter 2 must be a json object"); - if (A::EvalCode() == EVAL_ASSETSV2 || TokensIsVer1Active(NULL)) - return AssetOrders(tokenid, emptypk, evalcodeNFT); - else - return tokensv0::AssetOrders(tokenid, emptypk, evalcodeNFT); + // check unused params: + for (int i = 0; i < jsonParams.getKeys().size(); i ++) + if (acceptable.count(jsonParams.getKeys()[i]) == 0) + throw runtime_error(std::string("invalid json param") + jsonParams.getKeys()[i]); + } + return AssetOrders(tokenid, emptypk, jsonParams); } UniValue tokenorders(const UniValue& params, bool fHelp, const CPubKey& remotepk) @@ -177,23 +251,18 @@ UniValue tokenv2orders(const UniValue& params, bool fHelp, const CPubKey& remote template UniValue mytokenorders(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& remotepk) { - uint256 tokenid; - if (fHelp || params.size() > 1) - throw runtime_error(name + " [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(A::EvalCode()) < 0 || ensure_CCrequirements(T::EvalCode()) < 0) + if (fHelp || params.size() > 0) + throw runtime_error(name + "\n" + "returns all tokens orders for mypubkey\n" + // no additional evalcode for mytokenorders - it will return all orders for on mypk: + /*"if evalcode is set then returns mypubkey's token orders for non-fungible tokens with this evalcode\n"*/ "\n"); + if (ensure_CCrequirements(A::EvalCode(), remotepk.IsValid()) < 0 || ensure_CCrequirements(T::EvalCode(), remotepk.IsValid()) < 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 (A::EvalCode() == EVAL_ASSETSV2 || TokensIsVer1Active(NULL)) - return AssetOrders(zeroid, mypk, evalcodeNFT); - else - return tokensv0::AssetOrders(zeroid, Mypubkey(), evalcodeNFT); + return AssetOrders(zeroid, mypk, NullUniValue); } UniValue mytokenorders(const UniValue& params, bool fHelp, const CPubKey& remotepk) @@ -208,31 +277,40 @@ UniValue mytokenv2orders(const UniValue& params, bool fHelp, const CPubKey& remo template static UniValue tokenbalance(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& remotepk) { - UniValue result(UniValue::VOBJ); uint256 tokenid; uint64_t balance; std::vector vpubkey; struct CCcontract_info *cp,C; - CCerror.clear(); + UniValue result(UniValue::VOBJ); + CCerror.clear(); - if ( fHelp || params.size() < 1 || params.size() > 2 ) + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error(name + " tokenid [pubkey]\n"); - if ( ensure_CCrequirements(V::EvalCode()) < 0 ) + if (ensure_CCrequirements(V::EvalCode(), remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - - //LOCK(cs_main); - tokenid = Parseuint256((char *)params[0].get_str().c_str()); - if ( params.size() == 2 ) + // LOCK(cs_main); + // no need to lock cs_main as we use only indexes in this rpc + // but still use lock if you need to get chainActive.Height() or something like that + + uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); + std::vector vpubkey; + if (params.size() == 2) vpubkey = ParseHex(params[1].get_str().c_str()); - else - vpubkey = Mypubkey(); + else + vpubkey = Mypubkey(); - balance = GetTokenBalance(pubkey2pk(vpubkey), tokenid, false); + CPubKey pk = pubkey2pk(vpubkey); + if (!pk.IsValid()) return MakeResultError("invalid pubkey"); + CAmount balance = GetTokenBalance(pk, tokenid, false); if (CCerror.empty()) { char destaddr[KOMODO_ADDRESS_BUFSIZE]; + struct CCcontract_info *cp, C; + cp = CCinit(&C, V::EvalCode()); result.push_back(Pair("result", "success")); - cp = CCinit(&C, V::EvalCode()); - if (GetCCaddress(cp, destaddr, pubkey2pk(vpubkey), V::IsMixed()) != 0) - result.push_back(Pair("CCaddress", destaddr)); + + std::vector tokenindexkeys = V::GetTokenIndexKeys(pk); + UniValue uIndexkeys(UniValue::VARR); + for (auto const &tokenindexkey : tokenindexkeys) uIndexkeys.push_back(tokenindexkey); + result.push_back(Pair("CCIndexKeys", uIndexkeys)); result.push_back(Pair("tokenid", params[0].get_str())); result.push_back(Pair("balance", (int64_t)balance)); @@ -254,67 +332,197 @@ UniValue tokenv2balance(const UniValue& params, bool fHelp, const CPubKey& remot } template -static UniValue tokencreate(const std::string& fname, const UniValue& params, bool fHelp, const CPubKey& remotepk) +static UniValue tokenallbalances(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || params.size() > 1) + throw runtime_error(name + " [pubkey]\n"); + if (ensure_CCrequirements(V::EvalCode(), remotepk.IsValid()) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + + // LOCK(cs_main); + // no need to lock cs_main as we use only indexes in this rpc + // but still use lock if you need to get chainActive.Height() or something like that + + std::vector vpubkey; + if (params.size() == 1) + vpubkey = ParseHex(params[0].get_str().c_str()); + else + vpubkey = Mypubkey(); + + UniValue result = GetAllTokenBalances(pubkey2pk(vpubkey), false); + return result; +} + +UniValue tokenallbalances(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + return tokenallbalances("tokenallbalances", params, fHelp, remotepk); +} +UniValue tokenv2allbalances(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + return tokenallbalances("tokenv2allbalances", params, fHelp, remotepk); +} + + + +template +static UniValue tokencreate(const UniValue& params, const vuint8_t &vtokenData, 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 + std::string name, description; + CAmount supply; // changed from uin64_t to int64_t for this 'if ( supply <= 0 )' to work as expected + const CPubKey nullpk; + const CAmount txfee = 0; CCerror.clear(); - if ( fHelp || params.size() > 4 || params.size() < 2 ) - throw runtime_error(fname + " name supply [description][nft data]\n"); - if ( ensure_CCrequirements(V::EvalCode()) < 0 ) + //if (fHelp || params.size() > 4 || params.size() < 2) + // throw runtime_error(fname + " name supply [description] [tokens data]\n"); + if (ensure_CCrequirements(V::EvalCode(), remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - - if (!EnsureWalletIsAvailable(false)) + + if (!remotepk.IsValid() && !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"); + if (name.size() == 0 || name.size() > TOKENS_MAX_NAME_LENGTH) + return MakeResultError("Token name must not be empty and up to " + std::to_string(TOKENS_MAX_DESC_LENGTH)); 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 <= " + std::to_string(4096)); // allowed > MAX_SCRIPT_ELEMENT_SIZE - } - - if (params.size() >= 4) { - nonfungibleData = ParseHex(params[3].get_str()); - // looks like bigger than 520 data is available in opreturn - // if (nonfungibleData.size() > MAX_SCRIPT_ELEMENT_SIZE) // script element limit - // return MakeResultError("Non-fungible data size must be <= " + std::to_string(MAX_SCRIPT_ELEMENT_SIZE)); - - if( nonfungibleData.empty() ) - return MakeResultError("Non-fungible data incorrect"); + if (description.size() > TOKENS_MAX_DESC_LENGTH) + return MakeResultError("Token description must be <= " + std::to_string(TOKENS_MAX_DESC_LENGTH)); // allowed > MAX_SCRIPT_ELEMENT_SIZE } - hextx = CreateTokenLocal(0, supply, name, description, nonfungibleData); + //hextx = CreateTokenLocal(0, supply, name, description, vtokenData); + UniValue rcreate = CreateTokenExt(remotepk.IsValid() ? remotepk : nullpk, txfee, supply, name, description, vtokenData, 0, false); RETURN_IF_ERROR(CCerror); - if( hextx.size() > 0 ) - return MakeResultSuccess(hextx); - else - return MakeResultError("could not create token"); + if (remotepk.IsValid()) + return rcreate; + else { + std::string hextx = ResultGetTx(rcreate); + if( hextx.size() > 0 ) + return MakeResultSuccess(hextx); + else + return MakeResultError("could not create token"); + } } UniValue tokencreate(const UniValue& params, bool fHelp, const CPubKey& remotepk) { - return tokencreate("tokencreate", params, fHelp, remotepk); + if (fHelp || params.size() > 4 || params.size() < 2) + throw runtime_error("tokencreate name supply [description] [tokens data]\n" + "create tokens\n" + " name - token name string\n" + " supply - token supply in coins\n" + " description - optional description" + " tokens data - an optional hex string with token data. If the first byte is non-null it means a evalcode which these tokens will be routed into\n" + ); + + vuint8_t tokenData; + if (params.size() >= 4) { + tokenData = ParseHex(params[3].get_str()); + if (tokenData.empty()) + return MakeResultError("Token data incorrect"); + } + return tokencreate(params, tokenData, fHelp, remotepk); } UniValue tokenv2create(const UniValue& params, bool fHelp, const CPubKey& remotepk) { - return tokencreate("tokenv2create", params, fHelp, remotepk); +if (fHelp || params.size() > 4 || params.size() < 2) + throw runtime_error("tokenv2create name supply [description] [tokens data]\n" + "create tokens version 2\n" + " name - token name string\n" + " supply - token supply in coins\n" + " description - optional description" + " tokens data - an optional hex string with token data. If the first byte is non-null it means a evalcode which these tokens will be routed into\n" + ); + + vuint8_t tokenData; + if (params.size() >= 4) { + tokenData = ParseHex(params[3].get_str()); + if (tokenData.empty()) + return MakeResultError("Tokel token data incorrect"); + } + return tokencreate(params, tokenData, fHelp, remotepk); } +UniValue tokencreatetokel(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || params.size() > 4 || params.size() < 2) + throw runtime_error("tokencreatetokel name supply [description] [tokens data]\n" + "create tokens with tokel project data\n" + " name - token name string\n" + " supply - token supply in coins\n" + " description - opt description" + " tokens data - an optional json object with tokel token properties:\n" + " { \"url\":, \"id\":, \"royalty\":, \"arbitrary\": }\n" + ); + + vuint8_t tokenData; + if (params.size() >= 4) { + UniValue jsonParams; + std::string sError; + + if (params[3].getType() == UniValue::VOBJ) + jsonParams = params[3].get_array(); + else if (params[3].getType() == UniValue::VSTR) { // json in quoted string '{...}' + if (!jsonParams.read(params[3].get_str().c_str())) + return MakeResultError("parameter 4 must be a valid json object\n"); + } + if (jsonParams.getType() != UniValue::VOBJ) + throw runtime_error("parameter 4 must be a json object\n"); + + tokenData = ParseTokelJson(jsonParams); + if (tokenData.empty()) + throw runtime_error("Tokel token data incorrect"); + if (!CheckTokelData(tokenData, sError)) + throw runtime_error("Tokel token data incorrect: " + sError); + } + + return tokencreate(params, tokenData, fHelp, remotepk); +} +UniValue tokenv2createtokel(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || params.size() > 4 || params.size() < 2) + throw runtime_error("tokenv2createtokel name supply [description] [tokens data]\n" + "create tokens with tokel project data\n" + " name - token name string\n" + " supply - token supply in coins\n" + " description - opt description" + " tokens data - an opt json object with with tokel token properties:\n" + " { \"url\":, \"id\":, \"royalty\":, \"arbitrary\": }\n" + ); + + vuint8_t tokenData; + if (params.size() >= 4) { + UniValue jsonParams; + std::string sError; + + if (params[3].getType() == UniValue::VOBJ) + jsonParams = params[3].get_array(); + else if (params[3].getType() == UniValue::VSTR) { // json in quoted string '{...}' + if (!jsonParams.read(params[3].get_str().c_str())) + return MakeResultError("parameter 4 must be a valid json object\n"); + } + if (jsonParams.getType() != UniValue::VOBJ) + return MakeResultError("parameter 4 must be a json object\n"); + + tokenData = ParseTokelJson(jsonParams); + if (tokenData.empty()) + return MakeResultError("Token data incorrect"); + if (!CheckTokelData(tokenData, sError)) + throw runtime_error("Tokel token data incorrect: " + sError); + } + return tokencreate(params, tokenData, fHelp, remotepk); +} + + template static UniValue tokentransfer(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& remotepk) { @@ -323,110 +531,58 @@ static UniValue tokentransfer(const std::string& name, const UniValue& params, b CCerror.clear(); - if (fHelp || (params.size() != 3 && params.size() != 1)) - throw runtime_error(name + " tokenid destpubkey amount\n" + - name + " '{ \"tokenid\":\"\", \"ccaddressMofN\":\"
\", \"destpubkeys\": [ \"\", \"\", ... ], \"M\": , \"amount\": }'\n" - "tokenid - token creation id\n" - "ccaddressMofN - optional cc address of MofN utxos to spend, if not present spending is from mypk\n" - "destpubkey, destpubkey1 ... destpubkeyN - destination pubkeys (max = 128)\n" - "M - required min of signatures, integer\n\n" - "amount - token amount to send in satoshi, int64\n" - "Note, that MofN supported only for tokens v2\n\n"); + if (fHelp || params.size() != 3) + throw runtime_error( + name + " tokenid destination amount\n" + "To spend 1of1 token utxo and send to 1of1 destination. Params:\n" + " tokenid - token creation id\n" + " destination - destination pubkey or R-address\n" + " amount - token amount to send, in satoshi\n\n"); if (ensure_CCrequirements(V::EvalCode()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - if (!EnsureWalletIsAvailable(false)) + if (!remotepk.IsValid() && !EnsureWalletIsAvailable(false)) throw runtime_error("wallet is required"); - LOCK2(cs_main, pwalletMain->cs_wallet); // remote call not supported yet + LOCK2(cs_main, pwalletMain->cs_wallet); // remote call not supported, only local wallet + + uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); + if( tokenid.IsNull() ) + return MakeResultError("invalid tokenid"); - if (params.size() == 3) - { - uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); - if( tokenid == zeroid ) - return MakeResultError("invalid tokenid"); + std::vector dests; + + CTxDestination dest; + const int64_t nTime = GetTime(); + const int32_t nHeight = chainActive.LastTip()->GetHeight() + 1; + vuint8_t vpubkey(ParseHex(params[1].get_str())); + if (vpubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && CPubKey(vpubkey).IsValid()) + dest = CPubKey(vpubkey); + else + dest = DecodeDestination(params[1].get_str()); + if (dest.which() == TX_PUBKEYHASH && !CCUpgrades::IsUpgradeActive(nTime, nHeight, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)) + return MakeResultError("destination as address not active yet"); + if (dest.which() != TX_PUBKEYHASH && dest.which() != TX_PUBKEY) + return MakeResultError("invalid destination pubkey or address"); + if (V::EvalCode() == EVAL_TOKENS && dest.which() != TX_PUBKEY) // address not supported for tokens 1 + return MakeResultError("invalid destination pubkey"); - std::vector pks; - vuint8_t vpubkey(ParseHex(params[1].get_str().c_str())); - if (vpubkey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) - return MakeResultError("invalid destpubkey"); - pks.push_back(pubkey2pk(vpubkey)); - - CAmount amount = atoll(params[2].get_str().c_str()); - if( amount <= 0 ) - return MakeResultError("amount must be positive"); - hex = TokenTransfer(0, tokenid, 1, pks, amount); - RETURN_IF_ERROR(CCerror); - if (!hex.empty()) - return MakeResultSuccess(hex); - else - return MakeResultError("could not create transfer token transaction"); - } + // after subver_1 upgrade only addresses always will be used as destination, pubkeys are converted to addresses (for tokens v2) + if (V::EvalCode() == EVAL_TOKENSV2 && dest.which() == TX_PUBKEY && CCUpgrades::IsUpgradeActive(nTime, nHeight, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)) + dests.push_back(boost::get(dest).GetID()); else - { - if (V::EvalCode() != EVAL_TOKENSV2) - throw runtime_error("MofN transfer is supported only for tokens v2\n"); + dests.push_back(dest); // address or pubkey - UniValue jsonParams(UniValue::VOBJ); - if (params[0].getType() == UniValue::VOBJ) - jsonParams = params[0].get_obj(); - else if (params[0].getType() == UniValue::VSTR) // json in quoted string '{...}' - jsonParams.read(params[0].get_str().c_str()); - if (jsonParams.getType() != UniValue::VOBJ) - throw runtime_error("parameter 1 must be object\n"); - - uint256 tokenid = Parseuint256(jsonParams["tokenid"].get_str().c_str()); - if( tokenid == zeroid ) - return MakeResultError("invalid tokenid"); - - std::string ccaddressMofN; - if (!jsonParams["ccaddressMofN"].isNull()) { - ccaddressMofN = jsonParams["ccaddressMofN"].get_str(); - if (!CBitcoinAddress(ccaddressMofN).IsValid()) - throw runtime_error("invalid ccaddressMofN\n"); - } - - std::vector pks; - UniValue udestpks = jsonParams["destpubkeys"]; - if (!udestpks.isArray()) - throw runtime_error("destpubkeys must be an array\n"); - - if (udestpks.size() > 128) - throw runtime_error("destpubkeys num is limited by 128\n"); - - for (int i = 0; i < udestpks.size(); i ++) { - vuint8_t vpubkey(ParseHex(udestpks[i].get_str().c_str())); - if (vpubkey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) - return MakeResultError(std::string("invalid destpubkey #") + std::to_string(i+1)); - pks.push_back(CPubKey(vpubkey)); - } - int M = jsonParams["M"].get_int(); - if (M > 128) - throw runtime_error("M is limited by 128\n"); - if (M > pks.size()) - throw runtime_error("M could not be more than dest pubkeys\n"); - - CAmount amount = jsonParams["amount"].get_int64(); - if( amount <= 0 ) - return MakeResultError("amount must be positive"); - - if (ccaddressMofN.empty()) { - hex = TokenTransfer(0, tokenid, M, pks, amount); - RETURN_IF_ERROR(CCerror); - if (!hex.empty()) - return MakeResultSuccess(hex); - else - return MakeResultError("could not create transfer token transaction"); - } - else { - UniValue transferred = TokenTransferExt(CPubKey(), 0, tokenid, ccaddressMofN.c_str(), {}, (uint8_t)M, pks, amount, false); - RETURN_IF_ERROR(CCerror); - if (!ResultGetTx(transferred).empty()) - return transferred; - else - return MakeResultError("could not create transfer token transaction"); - } - } + CAmount amount = atoll(params[2].get_str().c_str()); + if( amount <= 0 ) + return MakeResultError("amount must be positive"); + const bool spendMarker = false; + hex = TokenTransferDest(0, tokenid, 1, dests, amount, spendMarker); + RETURN_IF_ERROR(CCerror); + if (!hex.empty()) + return MakeResultSuccess(hex); + else + return MakeResultError("could not create transfer token transaction"); } UniValue tokentransfer(const UniValue& params, bool fHelp, const CPubKey& remotepk) @@ -449,9 +605,9 @@ UniValue tokentransfermany(const std::string& name, const UniValue& params, bool CCerror.clear(); - if ( fHelp || params.size() < 3) + if (fHelp || params.size() < 3) throw runtime_error(name + " tokenid1 tokenid2 ... destpubkey amount \n"); - if ( ensure_CCrequirements(V::EvalCode()) < 0 ) + if (ensure_CCrequirements(V::EvalCode(), remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); std::vector tokenids; @@ -471,7 +627,7 @@ UniValue tokentransfermany(const std::string& name, const UniValue& params, bool if( amount <= 0 ) return MakeResultError("amount must be positive"); - if (!EnsureWalletIsAvailable(false)) + if (!remotepk.IsValid() && !EnsureWalletIsAvailable(false)) throw runtime_error("wallet is required"); LOCK2(cs_main, pwalletMain->cs_wallet); // remote call not supported yet @@ -485,29 +641,36 @@ UniValue tokentransfermany(const std::string& name, const UniValue& params, bool if (ResultIsError(beginResult)) return beginResult; - for (const auto &tokenid : tokenids) - { - TokenDataTuple tokenData; - vuint8_t vnftData; - GetTokenData(tokenid, tokenData, vnftData); + uint8_t mypriv[32]; + Myprivkey(mypriv); + std::vector srctokenaddrs; + std::vector> probes; + if (V::EvalCode() == EVAL_TOKENS) { CCwrapper probeCond; - if (vnftData.size() > 0) - probeCond.reset( V::MakeTokensCCcond1(vnftData[0], mypk) ); - else - probeCond.reset( MakeCCcond1(V::EvalCode(), mypk) ); + probeCond.reset( MakeCCcond1(V::EvalCode(), mypk) ); + //char tokenaddr[KOMODO_ADDRESS_BUFSIZE]; + //GetTokensCCaddress(cpTokens, tokenaddr, mypk, V::IsMixed()); + //srctokenaddrs.push_back(tokenaddr); + srctokenaddrs = GetTokenV1IndexKeys(mypk); - uint8_t mypriv[32]; - Myprivkey(mypriv); - - char tokenaddr[KOMODO_ADDRESS_BUFSIZE]; - cpTokens->evalcodeNFT = vnftData.size() > 0 ? vnftData[0] : 0; - GetTokensCCaddress(cpTokens, tokenaddr, mypk, V::IsMixed()); + probes.push_back({probeCond, mypriv}); + + } + else { + std::vector pkconds = GetTokenV2Conds(mypk); + srctokenaddrs = GetTokenV2IndexKeys(mypk); + for(auto const &cond : pkconds) + probes.push_back({cond, mypriv}); + } - UniValue addtxResult = TokenAddTransferVout(mtx, cpTokens, remotepk, tokenid, tokenaddr, { destpk }, {probeCond, mypriv}, amount, false); + for (const auto &tokenid : tokenids) + { + UniValue addtxResult = TokenAddTransferVout(mtx, cpTokens, remotepk, tokenid, srctokenaddrs, { destpk }, probes, amount, false); memset(mypriv, '\0', sizeof(mypriv)); if (ResultIsError(addtxResult)) return MakeResultError( ResultGetError(addtxResult) + " " + tokenid.GetHex() ); } + memset(mypriv, '\0', sizeof(mypriv)); UniValue sigData = TokenFinalizeTransferTx(mtx, cpTokens, remotepk, txfee, CScript()); RETURN_IF_ERROR(CCerror); if (ResultHasTx(sigData) > 0) @@ -526,44 +689,97 @@ UniValue tokenv2transfermany(const UniValue& params, bool fHelp, const CPubKey& return tokentransfermany("tokenv2transfermany", params, fHelp, remotepk); } -UniValue tokenconvert(const UniValue& params, bool fHelp, const CPubKey& mypk) + +UniValue tokenv2transferMofN(const UniValue& params, bool fHelp, const CPubKey& remotepk) { - UniValue result(UniValue::VOBJ); std::string hex; int32_t evalcode; int64_t amount; uint256 tokenid; + UniValue result(UniValue::VOBJ); + std::string hex; + CCerror.clear(); - if ( fHelp || params.size() != 4 ) - throw runtime_error("tokenconvert evalcode tokenid pubkey amount\n"); - if ( ensure_CCrequirements(EVAL_ASSETS) < 0 ) + + if (fHelp || params.size() != 1) + throw runtime_error( + std::string(__func__) + " '{ \"tokenid\":\"\", \"ccaddressMofN\":\"
\", \"destpubkeys\": [ \"\", \"\", ... ], \"M\": , \"amount\": }'\n" + "To spend MofN token utxo and send to MofN destination pubkeys. Params:\n" + " tokenid - token creation id\n" + " ccaddressMofN - optional cc address of MofN utxos to spend, if not present spending is from mypk\n" + " destpubkey, destpubkey1 ... destpubkeyN - destination pubkeys (max = 128)\n" + " M - required min number of signatures\n" + " amount - token amount to send, in satoshi\n" + "Note, that MofN supported only for tokens v2\n\n"); + + if (ensure_CCrequirements(TokensV2::EvalCode()) < 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 ) + + if (!remotepk.IsValid() && !EnsureWalletIsAvailable(false)) + throw runtime_error("wallet is required"); + + LOCK2(cs_main, pwalletMain->cs_wallet); // remote rpc call not supported, only local wallet + + UniValue jsonParams(UniValue::VOBJ); + if (params[0].getType() == UniValue::VOBJ) + jsonParams = params[0].get_obj(); + else if (params[0].getType() == UniValue::VSTR) { // json in quoted string '{...}' + if (!jsonParams.read(params[0].get_str().c_str())) + return MakeResultError("parameter 1 must be a valid json object\n"); + } + if (jsonParams.getType() != UniValue::VOBJ) + return MakeResultError("parameter 1 must be object\n"); + + uint256 tokenid = Parseuint256(jsonParams["tokenid"].get_str().c_str()); + if( tokenid == zeroid ) return MakeResultError("invalid tokenid"); + + std::string ccaddressMofN; + if (!jsonParams["ccaddressMofN"].isNull()) { + ccaddressMofN = jsonParams["ccaddressMofN"].get_str(); + if (!CBitcoinAddress(ccaddressMofN).IsValid()) + return MakeResultError("invalid ccaddressMofN\n"); + } - if( amount <= 0 ) - return MakeResultError("amount must be positive"); + std::vector pks; + UniValue udestpks = jsonParams["destpubkeys"]; + if (!udestpks.isArray()) + return MakeResultError("destpubkeys must be an array\n"); + + if (udestpks.size() > 128) + return MakeResultError("destpubkeys num is limited by 128\n"); - return MakeResultError("deprecated"); + for (int i = 0; i < udestpks.size(); i ++) { + vuint8_t vpubkey(ParseHex(udestpks[i].get_str().c_str())); + if (vpubkey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) + return MakeResultError(std::string("invalid destpubkey #") + std::to_string(i+1)); + pks.push_back(CPubKey(vpubkey)); + } + int M = jsonParams["M"].get_int(); + if (M > 128) + return MakeResultError("M is limited by 128\n"); + if (M > pks.size()) + return MakeResultError("M could not be more than dest pubkeys\n"); -/* 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"); + CAmount amount = jsonParams["amount"].get_int64(); + if( amount <= 0 ) + return MakeResultError("amount must be positive"); + + if (ccaddressMofN.empty()) { + hex = TokenTransfer(0, tokenid, M, pks, amount); + RETURN_IF_ERROR(CCerror); + if (!hex.empty()) + return MakeResultSuccess(hex); + else + return MakeResultError("could not create transfer token transaction"); } - return(result); */ + else { + UniValue transferred = TokenTransferExt(CPubKey(), 0, tokenid, { ccaddressMofN }, {}, (uint8_t)M, pks, amount, false); + RETURN_IF_ERROR(CCerror); + if (!ResultGetTx(transferred).empty()) + return transferred; + else + return MakeResultError("could not create transfer token transaction"); + } } + template UniValue tokenbid(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& remotepk) { @@ -573,12 +789,12 @@ UniValue tokenbid(const std::string& name, const UniValue& params, bool fHelp, c uint256 tokenid; CCerror.clear(); - if ( fHelp || params.size() != 3 ) - throw runtime_error(name + " numtokens tokenid price\n"); - if (ensure_CCrequirements(A::EvalCode()) < 0 || ensure_CCrequirements(T::EvalCode()) < 0) + if (fHelp || params.size() < 3 || params.size() > 4) + throw runtime_error(name + " numtokens tokenid price [expiry-height]\n"); + if (ensure_CCrequirements(A::EvalCode(), remotepk.IsValid()) < 0 || ensure_CCrequirements(T::EvalCode(), remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - if (!EnsureWalletIsAvailable(false)) + if (!remotepk.IsValid() && !EnsureWalletIsAvailable(false)) throw runtime_error("wallet is required"); CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); @@ -588,24 +804,26 @@ UniValue tokenbid(const std::string& name, const UniValue& params, bool fHelp, c 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"); + int32_t expiryHeight; + { + LOCK(cs_main); + expiryHeight = chainActive.Height() + 4 * 7 * 24 * 60; // 4 weeks for blocktime 60 sec + } + if (params.size() == 4) { + expiryHeight = atol(params[3].get_str().c_str()); + if (!remotepk.IsValid() && expiryHeight < chainActive.LastTip()->GetHeight()) + return MakeResultError("expiry height invalid"); + + } + CPubKey mypk; SET_MYPK_OR_REMOTE(mypk, remotepk); - if (A::EvalCode() == EVAL_ASSETSV2 || 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"); - } + result = CreateBuyOffer(mypk, 0, bidamount, tokenid, numtokens, expiryHeight); RETURN_IF_ERROR(CCerror); return result; } @@ -627,10 +845,10 @@ UniValue tokencancelbid(const std::string& name, const UniValue& params, bool fH if (fHelp || params.size() != 2) throw runtime_error(name + " tokenid bidtxid\n"); - if (ensure_CCrequirements(A::EvalCode()) < 0 || ensure_CCrequirements(T::EvalCode()) < 0) + if (ensure_CCrequirements(A::EvalCode(), remotepk.IsValid()) < 0 || ensure_CCrequirements(T::EvalCode(), remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - if (!EnsureWalletIsAvailable(false)) + if (!remotepk.IsValid() && !EnsureWalletIsAvailable(false)) throw runtime_error("wallet is required"); CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); @@ -641,15 +859,7 @@ UniValue tokencancelbid(const std::string& name, const UniValue& params, bool fH CPubKey mypk; SET_MYPK_OR_REMOTE(mypk, remotepk); - if (A::EvalCode() == EVAL_ASSETSV2 || 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"); - } + result = CancelBuyOffer(mypk, 0,tokenid,bidtxid); RETURN_IF_ERROR(CCerror); return result; } @@ -675,10 +885,10 @@ UniValue tokenfillbid(const std::string& name, const UniValue& params, bool fHel if (fHelp || params.size() != 3 && params.size() != 4) throw runtime_error(name + " tokenid bidtxid fillamount [unit_price]\n"); - if (ensure_CCrequirements(A::EvalCode()) < 0 || ensure_CCrequirements(T::EvalCode()) < 0) + if (ensure_CCrequirements(A::EvalCode(), remotepk.IsValid()) < 0 || ensure_CCrequirements(T::EvalCode(), remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - if (!EnsureWalletIsAvailable(false)) + if (!remotepk.IsValid() && !EnsureWalletIsAvailable(false)) throw runtime_error("wallet is required"); CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); @@ -697,15 +907,7 @@ UniValue tokenfillbid(const std::string& name, const UniValue& params, bool fHel CPubKey mypk; SET_MYPK_OR_REMOTE(mypk, remotepk); - if (A::EvalCode() == EVAL_ASSETSV2 || 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"); - } + result = FillBuyOffer(mypk, 0, tokenid, bidtxid, fillamount, unit_price); RETURN_IF_ERROR(CCerror); return result; } @@ -728,12 +930,12 @@ UniValue tokenask(const std::string& name, const UniValue& params, bool fHelp, c uint256 tokenid; CCerror.clear(); - if (fHelp || params.size() != 3) - throw runtime_error(name + " numtokens tokenid price\n"); - if (ensure_CCrequirements(A::EvalCode()) < 0 || ensure_CCrequirements(T::EvalCode()) < 0) + if (fHelp || params.size() < 3 || params.size() > 4) + throw runtime_error(name + " numtokens tokenid price [expiry-height]\n"); + if (ensure_CCrequirements(A::EvalCode(), remotepk.IsValid()) < 0 || ensure_CCrequirements(T::EvalCode(), remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - if (!EnsureWalletIsAvailable(false)) + if (!remotepk.IsValid() && !EnsureWalletIsAvailable(false)) throw runtime_error("wallet is required"); CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); @@ -741,21 +943,29 @@ UniValue tokenask(const std::string& name, const UniValue& params, bool fHelp, c 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"); + if (tokenid == zeroid) + return MakeResultError("tokenid invalid"); + if (numtokens <= 0) + return MakeResultError("numtokens invalid"); + if (price <= 0) + return MakeResultError("price invalid"); + if (askamount <= 0) + return MakeResultError("askamount invalid"); + + int32_t expiryHeight; + { + LOCK(cs_main); + expiryHeight = chainActive.Height() + 4 * 7 * 24 * 60; // 4 weeks for blocktime 60 sec + } + if (params.size() == 4) { + expiryHeight = atol(params[3].get_str().c_str()); + if (!remotepk.IsValid() && expiryHeight < chainActive.LastTip()->GetHeight()) + return MakeResultError("expiry height invalid"); + } CPubKey mypk; SET_MYPK_OR_REMOTE(mypk, remotepk); - - if (A::EvalCode() == EVAL_ASSETSV2 || 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"); - } + result = CreateSell(mypk, 0, numtokens, tokenid, askamount, expiryHeight); RETURN_IF_ERROR(CCerror); return result; } @@ -778,7 +988,7 @@ UniValue tokenswapask(const UniValue& params, bool fHelp, const CPubKey& remotep CCerror.clear(); if (fHelp || params.size() != 4) throw runtime_error("tokenswapask numtokens tokenid otherid price\n"); - if (ensure_CCrequirements(EVAL_ASSETS) < 0) + if (ensure_CCrequirements(EVAL_ASSETS, remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); if (!EnsureWalletIsAvailable(false)) @@ -787,6 +997,7 @@ UniValue tokenswapask(const UniValue& params, bool fHelp, const CPubKey& remotep 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()); @@ -795,14 +1006,15 @@ UniValue tokenswapask(const UniValue& params, bool fHelp, const CPubKey& remotep hex = CreateSwap(0,numtokens,tokenid,otherid,askamount); RETURN_IF_ERROR(CCerror); if (price > 0 && numtokens > 0) { - if ( hex.size() > 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("couldnt create swap"); } else { ERR_RESULT("price and numtokens must be positive"); } + */ return result; } @@ -829,15 +1041,7 @@ UniValue tokencancelask(const std::string& name, const UniValue& params, bool fH CPubKey mypk; SET_MYPK_OR_REMOTE(mypk, remotepk); - if (A::EvalCode() == EVAL_ASSETSV2 || 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"); - } + result = CancelSell(mypk, 0, tokenid, asktxid); RETURN_IF_ERROR(CCerror); return(result); } @@ -861,10 +1065,10 @@ UniValue tokenfillask(const std::string& name, const UniValue& params, bool fHel if (fHelp || params.size() != 3 && params.size() != 4) throw runtime_error(name + " tokenid asktxid fillunits [unitprice]\n"); - if (ensure_CCrequirements(A::EvalCode()) < 0 || ensure_CCrequirements(T::EvalCode()) < 0) + if (ensure_CCrequirements(A::EvalCode(), remotepk.IsValid()) < 0 || ensure_CCrequirements(T::EvalCode(), remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - if (!EnsureWalletIsAvailable(false)) + if (!remotepk.IsValid() && !EnsureWalletIsAvailable(false)) throw runtime_error("wallet is required"); CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); @@ -881,15 +1085,7 @@ UniValue tokenfillask(const std::string& name, const UniValue& params, bool fHel CPubKey mypk; SET_MYPK_OR_REMOTE(mypk, remotepk); - if (A::EvalCode() == EVAL_ASSETSV2 || 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"); - } + result = FillSell(mypk, 0, tokenid, asktxid, fillunits, unit_price); RETURN_IF_ERROR(CCerror); return result; } @@ -913,7 +1109,7 @@ UniValue tokenfillswap(const UniValue& params, bool fHelp, const CPubKey& remote 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) + if (ensure_CCrequirements(EVAL_ASSETS, remotepk.IsValid()) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); if (!EnsureWalletIsAvailable(false)) @@ -932,7 +1128,7 @@ UniValue tokenfillswap(const UniValue& params, bool fHelp, const CPubKey& remote 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); + result = FillSell(mypk, 0, tokenid, asktxid, fillunits, unit_price); RETURN_IF_ERROR(CCerror); if (fillunits > 0) { if ( hex.size() > 0 ) { @@ -962,8 +1158,10 @@ UniValue addccv2signature(const UniValue& params, bool fHelp, const CPubKey& rem vuint8_t vtx = ParseHex(params[0].get_str().c_str()); if (params[1].getType() == UniValue::VARR) jsonParams = params[1].get_array(); - else if (params[1].getType() == UniValue::VSTR) // json in quoted string '{...}' - jsonParams.read(params[1].get_str().c_str()); + else if (params[1].getType() == UniValue::VSTR) { // json in quoted string '{...}' + if (!jsonParams.read(params[1].get_str().c_str())) + return MakeResultError("parameter 2 must be a valid json object\n"); + } if (jsonParams.getType() != UniValue::VARR) throw runtime_error("parameter 2 must be array\n"); @@ -972,12 +1170,261 @@ UniValue addccv2signature(const UniValue& params, bool fHelp, const CPubKey& rem } +UniValue tokenv2addccinputs(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + if (fHelp || params.size() != 3) + { + string msg = "tokenv2addccinputs tokenid pubkey amount\n" + "\nReturns a new tx with added token inputs and the matching previous txns. Note that the caller must add the change output\n" + "\nArguments:\n" + //"address which utxos are added from\n" + "amount (in satoshi) which will be added as normal inputs (equal or more)\n" + "Result: json object with created tx and added vin txns\n\n"; + throw runtime_error(msg); + } + + if (ensure_CCrequirements(EVAL_TOKENSV2, remotepk.IsValid()) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + + uint256 tokenid = Parseuint256(params[0].get_str().c_str()); + CPubKey pk = pubkey2pk( ParseHex(params[1].get_str().c_str()) ); + CAmount amount = atoll(params[2].get_str().c_str()); + if (amount <= 0) + throw runtime_error("amount invalid"); + + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + struct CCcontract_info *cp, C; + cp = CCinit(&C, EVAL_TOKENSV2); + + CAmount added = AddTokenCCInputs(cp, mtx, pk, tokenid, amount, CC_MAXVINS, false); + if (added < amount) + throw runtime_error("could not find token cc inputs"); + + UniValue result (UniValue::VOBJ); + UniValue array (UniValue::VARR); + + result.pushKV("txhex", HexStr(E_MARSHAL(ss << mtx))); + for (auto const &vin : mtx.vin) { + CTransaction vintx; + uint256 hashBlock; + if (myGetTransaction(vin.prevout.hash, vintx, hashBlock)) + array.push_back(HexStr(E_MARSHAL(ss << vintx))); + } + result.pushKV("previousTxns", array); + return result; +} + +template +static UniValue tokenburn(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + UniValue result(UniValue::VOBJ); + std::string hex; + + CCerror.clear(); + + if (fHelp || params.size() != 3) + throw runtime_error( + name + " tokenid amount remove\n" + "to burn token amount by sending to a burn pubkey:\n" + " tokenid - token creation id\n" + " amount - token amount to burn, in satoshi\n" + " remove - remove from tokenlist by burning the marker utxo (true|false)\n\n"); + + if (ensure_CCrequirements(V::EvalCode()) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + + if (!remotepk.IsValid() && !EnsureWalletIsAvailable(false)) + throw runtime_error("wallet is required"); + LOCK2(cs_main, pwalletMain->cs_wallet); // remote call not supported, only local wallet + + uint256 tokenid = Parseuint256(params[0].get_str().c_str()); + if( tokenid.IsNull() ) + return MakeResultError("invalid tokenid"); + + std::vector dests; + + CPubKey dest; + vuint8_t vpubkey(ParseHex(CC_BURNPUBKEY_FIXED)); + if (!CPubKey(vpubkey).IsFullyValid()) + return MakeResultError("invalid burn pubkey"); + + dest = CPubKey(vpubkey); + dests.push_back(dest); + + CAmount amount = atoll(params[1].get_str().c_str()); + if( amount <= 0 ) + return MakeResultError("amount must be positive"); + std::string strSpend = params[2].get_str(); + std::transform(strSpend.begin(), strSpend.end(), strSpend.begin(), ::tolower); + const bool spendMarker = strSpend == "true" ? true : false; + hex = TokenTransferDest(0, tokenid, 1, dests, amount, spendMarker); + RETURN_IF_ERROR(CCerror); + if (!hex.empty()) + return MakeResultSuccess(hex); + else + return MakeResultError("could not create burn token transaction"); +} + +UniValue tokenburn(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + return tokenburn(__func__, params, fHelp, remotepk); +} +UniValue tokenv2burn(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + return tokenburn(__func__, params, fHelp, remotepk); +} + + +// cc tx creation helper rpc (test) +UniValue CreateCCEvalTx(const CPubKey &mypk, CAmount txfee, const UniValue &txjson) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + struct CCcontract_info *cpEvals, C; + + CAmount inputs = 0LL; + CAmount outputs = 0LL; + + + cpEvals = CCinit(&C, EVAL_TOKENSV2); + if (txfee == 0) + txfee = 10000; + + UniValue jvins = txjson[std::string("vins")]; + if (!jvins.isArray()) { CCerror = "no or incorrect 'vins' array"; return NullUniValue; } + UniValue jvouts = txjson[std::string("vouts")]; + if (!jvouts.isArray()) { CCerror = "no or incorrect 'vouts' array"; return NullUniValue; } + + for (int i = 0; i < jvins.size(); i ++) { + uint256 vintxid = Parseuint256(jvins[i][std::string("hash")].get_str().c_str()); + //std::cerr << __func__ << " getting n" << std::endl; + int32_t vini = jvins[i][std::string("n")].get_int(); + //std::cerr << __func__ << " got n" << std::endl; + uint256 hashBlock; + CTransaction vintx; + if (!myGetTransaction(vintxid, vintx, hashBlock)) { CCerror = "could not load vin tx:" + vintxid.GetHex(); return NullUniValue; } + inputs += vintx.vout[vini].nValue; + + mtx.vin.push_back(CTxIn(vintxid, vini)); + } + + for (int i = 0; i < jvouts.size(); i ++) { + UniValue uniAmount; + if (!(uniAmount = jvouts[i][std::string("nValue")]).empty()) { CCerror = "no nValue in vout"; return NullUniValue; } + //std::cerr << __func__ << " getting nValue " << uniAmount.write() << std::endl; + CAmount nValue = uniAmount.get_int64(); + //std::cerr << __func__ << " got nValue" << std::endl; + UniValue uniDest; + UniValue uniCC; + bool hasPkh = false, hasCC = false; + + if (!(uniDest = jvouts[i][std::string("Destination")]).isNull()) { + CTxDestination dest = DecodeDestination(uniDest.get_str()); + //if (dest.which() != TX_PUBKEYHASH) { CCerror = "only address destinations supported"; return NullUniValue; } + CScript script = GetScriptForDestination(dest); + if (script.empty()) { CCerror = "could not get script for normal destination"; return NullUniValue; } + mtx.vout.push_back(CTxOut(nValue, script)); + hasPkh = true; + } + else if (!(uniCC = jvouts[i][std::string("cc")]).isNull()) { + std::string ccstr = uniCC.write(); + char ccerr[128]; + CC *cond = cc_conditionFromJSONString(ccstr.c_str(), ccerr); + if (!cond) { CCerror = strprintf("could parse cc: %s", ccerr); return NullUniValue; } + + CScript script; + if (!jvouts[i]["opdrop"].isNull()) { + script = CCPubKey(cond, CC_OLD_V1_SUBVER); + std::vector vvopdrop = { ParseHex(jvouts[i]["opdrop"].get_str()) }; + COptCCParams ccp = COptCCParams(COptCCParams::VERSION_2, EVAL_TOKENSV2, 1, 1, { CPubKey(ParseHex(jvouts[i]["pubkey"].get_str())) }, vvopdrop); + script << ccp.AsVector() << OP_DROP; + } + else + script = CCPubKey(cond, CC_MIXED_MODE_SECHASH_SUBVER_1); // use subver 1 + if (script.empty()) { CCerror = "could not get script for cc"; return NullUniValue; } + mtx.vout.push_back(CTxOut(nValue, script)); + hasCC = true; + } + else { + CCerror = strprintf("invalid destination type for vout %d", i); return NullUniValue; + } + if (hasPkh && hasCC) { CCerror = "could not have both normal and cc destinations for one vout"; return NullUniValue; } + outputs += mtx.vout[i].nValue; + } + + if (inputs < outputs + txfee) { + if (AddNormalinputs(mtx, mypk, outputs + txfee - inputs, 0x10000) <= 0) { CCerror = "could not get normal inputs"; return NullUniValue; } + } + + const uint8_t nullpriv[32] = {'\0'}; + const uint8_t dontsign[32] = { 0xff }; + + // parse probe conds: + UniValue jvinccs = txjson[std::string("vinccs")]; + if (!jvinccs.isArray()) { CCerror = "no or incorrect 'vinccs' array"; return NullUniValue; } + for (int i = 0; i < jvinccs.size(); i ++) { + UniValue uniCC; + if (!(uniCC = jvinccs[i][std::string("cc")]).isNull()) { + std::string ccstr = uniCC.write(); + char ccerr[128]; + CCwrapper wrcond( cc_conditionFromJSONString(ccstr.c_str(), ccerr) ); + if (!wrcond.get()) { CCerror = strprintf("could parse vin cc: %s", ccerr); ; return NullUniValue; } + //std::cerr << __func__ << " getting sign" << std::endl; + bool bSign = jvinccs[i][std::string("sign")].get_bool(); + //std::cerr << __func__ << " got sign" << std::endl; + CCAddVintxCond(cpEvals, wrcond, bSign ? nullpriv : dontsign); // add a probe cond how to spend vintx cc utxo + } + } + + UniValue sigData = FinalizeCCV2Tx(false, FINALIZECCTX_NO_CHANGE_WHEN_DUST, cpEvals, mtx, mypk, txfee, CScript()); + if (!ResultHasTx(sigData)) + return MakeResultError("Could not finalize tx"); + return sigData; +} + +UniValue createccevaltx(const UniValue& params, bool fHelp, const CPubKey& remotepk) +{ + UniValue result(UniValue::VOBJ); + + if (fHelp || params.size() != 1) + throw std::runtime_error(std::string(__func__) + " 'json'\n" + "create and sign transaction with cc evals. The param is a json object with tx vin vout props:\n" + "'{ \"vins\": [...], \"vouts\": [...], \"vinccs\": [...] }'\n" + "'vins' - vin array in the format: '\"vins\":[{\"hash\": prev-tx-hash, \"n\": prev-utxo-n }, {...}]'\n" + "'vouts' - vout array in the format: '\"vouts\":[{\"nValue\": satoshis, \"Destination\": address-or-pubkey, \"cc\": condition-in-json }, {...}]'\n" + "'vinccs' - array of cc used for spending cc utxos, the format is: '\"vinccs\": [{ \"cc\": condition-in-json, \"sign\": true/false }, {..}]'\n\n"); + if (ensure_CCrequirements(EVAL_TOKENSV2) < 0) + throw std::runtime_error(CC_REQUIREMENTS_MSG); + + if (!remotepk.IsValid() && !EnsureWalletIsAvailable(false)) + throw std::runtime_error("wallet is required"); + CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !remotepk.IsValid()); + + UniValue jsonParams(UniValue::VOBJ); + if (params[0].getType() == UniValue::VOBJ) + jsonParams = params[0].get_obj(); + else if (params[0].getType() == UniValue::VSTR) { // json in quoted string '{...}' + if (!jsonParams.read(params[0].get_str().c_str())) + return MakeResultError("parameter must be a valid json object\n"); + } + if (jsonParams.getType() != UniValue::VOBJ) + return MakeResultError("parameter must be an object\n"); + + CPubKey mypk; + SET_MYPK_OR_REMOTE(mypk, remotepk); + + result = CreateCCEvalTx(mypk, 0, jsonParams); + RETURN_IF_ERROR(CCerror); + return result; +} + + + static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // -------------- ------------------------ ----------------------- ---------- // tokens & assets - { "tokens", "assetsaddress", &assetsaddress, true }, - { "tokens v2", "assetsv2address", &assetsv2address, true }, + { "tokens", "assetsindexkey", &assetsindexkey, true }, + { "tokens v2", "assetsv2indexkey", &assetsv2indexkey, true }, { "tokens", "tokeninfo", &tokeninfo, true }, { "tokens v2", "tokenv2info", &tokenv2info, true }, @@ -987,16 +1434,19 @@ static const CRPCCommand commands[] = { "tokens v2", "tokenv2orders", &tokenv2orders, true }, { "tokens", "mytokenorders", &mytokenorders, true }, { "tokens v2", "mytokenv2orders", &mytokenv2orders, true }, - { "tokens", "tokenaddress", &tokenaddress, true }, - { "tokens v2", "tokenv2address", &tokenv2address, true }, + { "tokens", "tokenindexkey", &tokenindexkey, true }, + { "tokens v2", "tokenv2indexkey", &tokenv2indexkey, true }, { "tokens", "tokenbalance", &tokenbalance, true }, { "tokens v2", "tokenv2balance", &tokenv2balance, true }, + { "tokens", "tokenallbalances", &tokenallbalances, true }, + { "tokens v2", "tokenv2allbalances", &tokenv2allbalances, true }, { "tokens", "tokencreate", &tokencreate, true }, { "tokens v2", "tokenv2create", &tokenv2create, true }, { "tokens", "tokentransfer", &tokentransfer, true }, { "tokens v2", "tokenv2transfer", &tokenv2transfer, true }, { "tokens", "tokentransfermany", &tokentransfermany, true }, { "tokens v2", "tokenv2transfermany", &tokenv2transfermany, true }, + { "tokens v2", "tokenv2transferMofN", &tokenv2transferMofN, true }, { "tokens", "tokenbid", &tokenbid, true }, { "tokens", "tokencancelbid", &tokencancelbid, true }, { "tokens", "tokenfillbid", &tokenfillbid, true }, @@ -1012,8 +1462,17 @@ static const CRPCCommand commands[] = { "tokens v2", "tokenv2cancelask", &tokenv2cancelask, true }, { "tokens v2", "tokenv2fillask", &tokenv2fillask, true }, //{ "tokens", "tokenfillswap", &tokenfillswap, true }, - { "tokens", "tokenconvert", &tokenconvert, true }, + //{ "tokens", "tokenconvert", &tokenconvert, true }, { "ccutils", "addccv2signature", &addccv2signature, true }, + { "tokens", "tokencreatetokel", &tokencreatetokel, true }, + { "tokens v2", "tokenv2createtokel", &tokenv2createtokel, true }, + { "tokens", "tokeninfotokel", &tokeninfotokel, true }, + { "tokens v2", "tokenv2infotokel", &tokenv2infotokel, true }, + { "tokens", "tokenburn", &tokenburn, true }, + { "tokens v2", "tokenv2burn", &tokenv2burn, true }, + { "nspv", "tokenv2addccinputs", &tokenv2addccinputs, true }, + { "nspv", "createccevaltx", &createccevaltx, true }, + }; void RegisterTokensRPCCommands(CRPCTable &tableRPC) diff --git a/src/rpcblockchain.old b/src/rpcblockchain.old deleted file mode 100644 index a91f73a6364..00000000000 --- a/src/rpcblockchain.old +++ /dev/null @@ -1,1625 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-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 "amount.h" -#include "chain.h" -#include "chainparams.h" -#include "checkpoints.h" -#include "crosschain.h" -#include "base58.h" -#include "consensus/validation.h" -#include "cc/eval.h" -#include "main.h" -#include "primitives/transaction.h" -#include "rpcserver.h" -#include "sync.h" -#include "util.h" -#include "script/script.h" -#include "script/script_error.h" -#include "script/sign.h" -#include "script/standard.h" - -#include - -#include - -#include - -using namespace std; - -extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); -void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); -int32_t komodo_longestchain(); -int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); -<<<<<<< HEAD:src/rpcblockchain.old -extern int32_t KOMODO_LONGESTCHAIN; -======= ->>>>>>> master:src/rpcblockchain.cpp - -double GetDifficultyINTERNAL(const CBlockIndex* blockindex, bool networkDifficulty) -{ - // Floating point number that is a multiple of the minimum difficulty, - // minimum difficulty = 1.0. - if (blockindex == NULL) - { - if (chainActive.LastTip() == NULL) - return 1.0; - else - blockindex = chainActive.LastTip(); - } - - uint32_t bits; - if (networkDifficulty) { - bits = GetNextWorkRequired(blockindex, nullptr, Params().GetConsensus()); - } else { - bits = blockindex->nBits; - } - - uint32_t powLimit = - UintToArith256(Params().GetConsensus().powLimit).GetCompact(); - int nShift = (bits >> 24) & 0xff; - int nShiftAmount = (powLimit >> 24) & 0xff; - - double dDiff = - (double)(powLimit & 0x00ffffff) / - (double)(bits & 0x00ffffff); - - while (nShift < nShiftAmount) - { - dDiff *= 256.0; - nShift++; - } - while (nShift > nShiftAmount) - { - dDiff /= 256.0; - nShift--; - } - - return dDiff; -} - -double GetDifficulty(const CBlockIndex* blockindex) -{ - return GetDifficultyINTERNAL(blockindex, false); -} - -double GetNetworkDifficulty(const CBlockIndex* blockindex) -{ - return GetDifficultyINTERNAL(blockindex, true); -} - -static UniValue ValuePoolDesc( - const std::string &name, - const boost::optional chainValue, - const boost::optional valueDelta) -{ - UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("id", name)); - rv.push_back(Pair("monitored", (bool)chainValue)); - if (chainValue) { - rv.push_back(Pair("chainValue", ValueFromAmount(*chainValue))); - rv.push_back(Pair("chainValueZat", *chainValue)); - } - if (valueDelta) { - rv.push_back(Pair("valueDelta", ValueFromAmount(*valueDelta))); - rv.push_back(Pair("valueDeltaZat", *valueDelta)); - } - return rv; -} - -UniValue blockheaderToJSON(const CBlockIndex* blockindex) -{ - UniValue result(UniValue::VOBJ); - if ( blockindex == 0 ) - { - result.push_back(Pair("error", "null blockhash")); - return(result); - } - 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->nHeight + 1; - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->nHeight,confirmations))); - result.push_back(Pair("rawconfirmations", confirmations)); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", blockindex->nVersion)); - result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex())); - result.push_back(Pair("time", (int64_t)blockindex->nTime)); - result.push_back(Pair("nonce", blockindex->nNonce.GetHex())); - result.push_back(Pair("solution", HexStr(blockindex->nSolution))); - result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); - result.push_back(Pair("segid", (int64_t)blockindex->segid)); - - if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - CBlockIndex *pnext = chainActive.Next(blockindex); - if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); - return result; -} - -UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) -{ - UniValue result(UniValue::VOBJ); - 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->nHeight + 1; - } else { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block is an orphan"); - } - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->nHeight,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->nHeight)); - result.push_back(Pair("version", block.nVersion)); - result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("segid", (int64_t)blockindex->segid)); - - UniValue deltas(UniValue::VARR); - - for (unsigned int i = 0; i < block.vtx.size(); i++) { - const CTransaction &tx = block.vtx[i]; - const uint256 txhash = tx.GetHash(); - - UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("txid", txhash.GetHex())); - entry.push_back(Pair("index", (int)i)); - - UniValue inputs(UniValue::VARR); - - if (!tx.IsCoinBase()) { - - for (size_t j = 0; j < tx.vin.size(); j++) { - const CTxIn input = tx.vin[j]; - - UniValue delta(UniValue::VOBJ); - - CSpentIndexValue spentInfo; - CSpentIndexKey spentKey(input.prevout.hash, input.prevout.n); - - if (GetSpentIndex(spentKey, spentInfo)) { - if (spentInfo.addressType == 1) { - delta.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString())); - } - else if (spentInfo.addressType == 2) { - delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); - } - else { - continue; - } - delta.push_back(Pair("satoshis", -1 * spentInfo.satoshis)); - delta.push_back(Pair("index", (int)j)); - delta.push_back(Pair("prevtxid", input.prevout.hash.GetHex())); - delta.push_back(Pair("prevout", (int)input.prevout.n)); - - inputs.push_back(delta); - } else { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Spent information not available"); - } - - } - } - - entry.push_back(Pair("inputs", inputs)); - - UniValue outputs(UniValue::VARR); - - for (unsigned int k = 0; k < tx.vout.size(); k++) { - const CTxOut &out = tx.vout[k]; - - UniValue delta(UniValue::VOBJ); - - if (out.scriptPubKey.IsPayToScriptHash()) { - 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); - delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); - } - else if (out.scriptPubKey.IsPayToPublicKey() || out.scriptPubKey.IsPayToCryptoCondition()) { - CTxDestination address; - if (ExtractDestination(out.scriptPubKey, address)) - { - //vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); - //xxx delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); - delta.push_back(Pair("address", CBitcoinAddress(address).ToString())); - } - } - else { - continue; - } - - delta.push_back(Pair("satoshis", out.nValue)); - delta.push_back(Pair("index", (int)k)); - - outputs.push_back(delta); - } - - entry.push_back(Pair("outputs", outputs)); - deltas.push_back(entry); - - } - result.push_back(Pair("deltas", deltas)); - result.push_back(Pair("time", block.GetBlockTime())); - result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); - result.push_back(Pair("nonce", block.nNonce.GetHex())); - result.push_back(Pair("bits", strprintf("%08x", block.nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); - - if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - CBlockIndex *pnext = chainActive.Next(blockindex); - if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); - return result; -} - -UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) -{ - UniValue result(UniValue::VOBJ); - 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->nHeight + 1; - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->nHeight,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->nHeight)); - result.push_back(Pair("version", block.nVersion)); - result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("segid", (int64_t)blockindex->segid)); - UniValue txs(UniValue::VARR); - BOOST_FOREACH(const CTransaction&tx, block.vtx) - { - if(txDetails) - { - UniValue objTx(UniValue::VOBJ); - TxToJSON(tx, uint256(), objTx); - txs.push_back(objTx); - } - else - txs.push_back(tx.GetHash().GetHex()); - } - result.push_back(Pair("tx", txs)); - result.push_back(Pair("time", block.GetBlockTime())); - result.push_back(Pair("nonce", block.nNonce.GetHex())); - result.push_back(Pair("solution", HexStr(block.nSolution))); - result.push_back(Pair("bits", strprintf("%08x", block.nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); - result.push_back(Pair("anchor", blockindex->hashAnchorEnd.GetHex())); - - UniValue valuePools(UniValue::VARR); - valuePools.push_back(ValuePoolDesc("sprout", blockindex->nChainSproutValue, blockindex->nSproutValue)); - result.push_back(Pair("valuePools", valuePools)); - - if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - CBlockIndex *pnext = chainActive.Next(blockindex); - if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); - return result; -} - -UniValue getblockcount(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblockcount\n" - "\nReturns the number of blocks in the best valid block chain.\n" - "\nResult:\n" - "n (numeric) The current block count\n" - "\nExamples:\n" - + HelpExampleCli("getblockcount", "") - + HelpExampleRpc("getblockcount", "") - ); - - LOCK(cs_main); - return chainActive.Height(); -} - -UniValue getbestblockhash(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getbestblockhash\n" - "\nReturns the hash of the best (tip) block in the longest block chain.\n" - "\nResult\n" - "\"hex\" (string) the block hash hex encoded\n" - "\nExamples\n" - + HelpExampleCli("getbestblockhash", "") - + HelpExampleRpc("getbestblockhash", "") - ); - - LOCK(cs_main); - return chainActive.LastTip()->GetBlockHash().GetHex(); -} - -UniValue getdifficulty(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getdifficulty\n" - "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n" - "\nResult:\n" - "n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n" - "\nExamples:\n" - + HelpExampleCli("getdifficulty", "") - + HelpExampleRpc("getdifficulty", "") - ); - - LOCK(cs_main); - return GetNetworkDifficulty(); -} - -bool myIsutxo_spentinmempool(uint256 txid,int32_t vout) -{ - //char *uint256_str(char *str,uint256); char str[65]; - //LOCK(mempool.cs); - BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) - { - const CTransaction &tx = e.GetTx(); - const uint256 &hash = tx.GetHash(); - 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 ) - return(true); - } - //fprintf(stderr,"are vins for %s\n",uint256_str(str,hash)); - } - return(false); -} - -bool mytxid_inmempool(uint256 txid) -{ - BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) - { - const CTransaction &tx = e.GetTx(); - const uint256 &hash = tx.GetHash(); - if ( txid == hash ) - return(true); - } - return(false); -} - -UniValue mempoolToJSON(bool fVerbose = false) -{ - if (fVerbose) - { - LOCK(mempool.cs); - UniValue o(UniValue::VOBJ); - BOOST_FOREACH(const CTxMemPoolEntry& e, mempool.mapTx) - { - const uint256& hash = e.GetTx().GetHash(); - UniValue info(UniValue::VOBJ); - info.push_back(Pair("size", (int)e.GetTxSize())); - info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); - info.push_back(Pair("time", e.GetTime())); - info.push_back(Pair("height", (int)e.GetHeight())); - info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight()))); - info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height()))); - const CTransaction& tx = e.GetTx(); - set setDepends; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - if (mempool.exists(txin.prevout.hash)) - setDepends.insert(txin.prevout.hash.ToString()); - } - - UniValue depends(UniValue::VARR); - BOOST_FOREACH(const string& dep, setDepends) - { - depends.push_back(dep); - } - - info.push_back(Pair("depends", depends)); - o.push_back(Pair(hash.ToString(), info)); - } - return o; - } - else - { - vector vtxid; - mempool.queryHashes(vtxid); - - UniValue a(UniValue::VARR); - BOOST_FOREACH(const uint256& hash, vtxid) - a.push_back(hash.ToString()); - - return a; - } -} - -UniValue getrawmempool(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getrawmempool ( verbose )\n" - "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" - "\nArguments:\n" - "1. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n" - "\nResult: (for verbose = false):\n" - "[ (json array of string)\n" - " \"transactionid\" (string) The transaction id\n" - " ,...\n" - "]\n" - "\nResult: (for verbose = true):\n" - "{ (json object)\n" - " \"transactionid\" : { (json object)\n" - " \"size\" : n, (numeric) transaction size in bytes\n" - " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" - " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" - " \"height\" : n, (numeric) block height when transaction entered pool\n" - " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" - " \"currentpriority\" : n, (numeric) transaction priority now\n" - " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" - " \"transactionid\", (string) parent transaction id\n" - " ... ]\n" - " }, ...\n" - "}\n" - "\nExamples\n" - + HelpExampleCli("getrawmempool", "true") - + HelpExampleRpc("getrawmempool", "true") - ); - - LOCK(cs_main); - - bool fVerbose = false; - if (params.size() > 0) - fVerbose = params[0].get_bool(); - - return mempoolToJSON(fVerbose); -} - -UniValue getblockdeltas(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error(""); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlock block; - CBlockIndex* pblockindex = mapBlockIndex[hash]; - - 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)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); - - return blockToDeltasJSON(block, pblockindex); -} - -UniValue getblockhashes(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 2) - throw runtime_error( - "getblockhashes timestamp\n" - "\nReturns array of hashes of blocks within the timestamp range provided.\n" - "\nArguments:\n" - "1. high (numeric, required) The newer block timestamp\n" - "2. low (numeric, required) The older block timestamp\n" - "3. options (string, required) A json object\n" - " {\n" - " \"noOrphans\":true (boolean) will only include blocks on the main chain\n" - " \"logicalTimes\":true (boolean) will include logical timestamps with hashes\n" - " }\n" - "\nResult:\n" - "[\n" - " \"hash\" (string) The block hash\n" - "]\n" - "[\n" - " {\n" - " \"blockhash\": (string) The block hash\n" - " \"logicalts\": (numeric) The logical timestamp\n" - " }\n" - "]\n" - "\nExamples:\n" - + 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(); - bool fActiveOnly = false; - bool fLogicalTS = false; - - if (params.size() > 2) { - if (params[2].isObject()) { - UniValue noOrphans = find_value(params[2].get_obj(), "noOrphans"); - UniValue returnLogical = find_value(params[2].get_obj(), "logicalTimes"); - - if (noOrphans.isBool()) - fActiveOnly = noOrphans.get_bool(); - - if (returnLogical.isBool()) - fLogicalTS = returnLogical.get_bool(); - } - } - - std::vector > blockHashes; - - if (fActiveOnly) - LOCK(cs_main); - - if (!GetTimestampIndex(high, low, fActiveOnly, blockHashes)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for block hashes"); - } - - UniValue result(UniValue::VARR); - - 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 { - result.push_back(it->first.GetHex()); - } - } - - return result; -} - -UniValue getblockhash(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getblockhash index\n" - "\nReturns hash of block in best-block-chain at index provided.\n" - "\nArguments:\n" - "1. index (numeric, required) The block index\n" - "\nResult:\n" - "\"hash\" (string) The block hash\n" - "\nExamples:\n" - + HelpExampleCli("getblockhash", "1000") - + HelpExampleRpc("getblockhash", "1000") - ); - - LOCK(cs_main); - - int nHeight = params[0].get_int(); - if (nHeight < 0 || nHeight > chainActive.Height()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - - CBlockIndex* pblockindex = chainActive[nHeight]; - return pblockindex->GetBlockHash().GetHex(); -} - -/*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); -}*/ - -UniValue getblockheader(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getblockheader \"hash\" ( verbose )\n" - "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n" - "If verbose is true, returns an Object with information about blockheader .\n" - "\nArguments:\n" - "1. \"hash\" (string, required) The block hash\n" - "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" - "\nResult (for verbose = true):\n" - "{\n" - " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" - " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" - " \"height\" : n, (numeric) The block height or index\n" - " \"version\" : n, (numeric) The block version\n" - " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" - " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"nonce\" : n, (numeric) The nonce\n" - " \"bits\" : \"1d00ffff\", (string) The bits\n" - " \"difficulty\" : x.xxx, (numeric) The difficulty\n" - " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" - " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" - "}\n" - "\nResult (for verbose=false):\n" - "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - "\nExamples:\n" - + HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - ); - - LOCK(cs_main); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - - bool fVerbose = true; - if (params.size() > 1) - fVerbose = params[1].get_bool(); - - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlockIndex* pblockindex = mapBlockIndex[hash]; - - if (!fVerbose) - { - CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); - ssBlock << pblockindex->GetBlockHeader(); - std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); - return strHex; - } - - return blockheaderToJSON(pblockindex); -} - -UniValue getblock(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getblock \"hash|height\" ( verbose )\n" - "\nIf verbose is false, returns a string that is serialized, hex-encoded data for block 'hash|height'.\n" - "If verbose is true, returns an Object with information about block .\n" - "\nArguments:\n" - "1. \"hash|height\" (string, required) The block hash or height\n" - "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" - "\nResult (for verbose = true):\n" - "{\n" - " \"hash\" : \"hash\", (string) the block hash (same as provided hash)\n" - " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" - " \"size\" : n, (numeric) The block size\n" - " \"height\" : n, (numeric) The block height or index (same as provided height)\n" - " \"version\" : n, (numeric) The block version\n" - " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" - " \"tx\" : [ (array of string) The transaction ids\n" - " \"transactionid\" (string) The transaction id\n" - " ,...\n" - " ],\n" - " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"nonce\" : n, (numeric) The nonce\n" - " \"bits\" : \"1d00ffff\", (string) The bits\n" - " \"difficulty\" : x.xxx, (numeric) The difficulty\n" - " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" - " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" - "}\n" - "\nResult (for verbose=false):\n" - "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - "\nExamples:\n" - + HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - + HelpExampleCli("getblock", "12800") - + HelpExampleRpc("getblock", "12800") - ); - - LOCK(cs_main); - - std::string strHash = params[0].get_str(); - - // If height is supplied, find the hash - if (strHash.size() < (2 * sizeof(uint256))) { - // std::stoi allows characters, whereas we want to be strict - regex r("[[:digit:]]+"); - if (!regex_match(strHash, r)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block height parameter"); - } - - int nHeight = -1; - try { - nHeight = std::stoi(strHash); - } - catch (const std::exception &e) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block height parameter"); - } - - if (nHeight < 0 || nHeight > chainActive.Height()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - } - strHash = chainActive[nHeight]->GetBlockHash().GetHex(); - } - - uint256 hash(uint256S(strHash)); - - bool fVerbose = true; - if (params.size() > 1) - fVerbose = params[1].get_bool(); - - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlock block; - CBlockIndex* pblockindex = mapBlockIndex[hash]; - - 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)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); - - if (!fVerbose) - { - CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); - ssBlock << block; - std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); - return strHex; - } - - return blockToJSON(block, pblockindex); -} - -UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "gettxoutsetinfo\n" - "\nReturns statistics about the unspent transaction output set.\n" - "Note this call may take some time.\n" - "\nResult:\n" - "{\n" - " \"height\":n, (numeric) The current block height (index)\n" - " \"bestblock\": \"hex\", (string) the best block hash hex\n" - " \"transactions\": n, (numeric) The number of transactions\n" - " \"txouts\": n, (numeric) The number of output transactions\n" - " \"bytes_serialized\": n, (numeric) The serialized size\n" - " \"hash_serialized\": \"hash\", (string) The serialized hash\n" - " \"total_amount\": x.xxx (numeric) The total amount\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("gettxoutsetinfo", "") - + HelpExampleRpc("gettxoutsetinfo", "") - ); - - UniValue ret(UniValue::VOBJ); - - CCoinsStats stats; - FlushStateToDisk(); - if (pcoinsTip->GetStats(stats)) { - ret.push_back(Pair("height", (int64_t)stats.nHeight)); - ret.push_back(Pair("bestblock", stats.hashBlock.GetHex())); - ret.push_back(Pair("transactions", (int64_t)stats.nTransactions)); - ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs)); - ret.push_back(Pair("bytes_serialized", (int64_t)stats.nSerializedSize)); - ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex())); - ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount))); - } - return ret; -} - -#include "komodo_defs.h" -#include "komodo_structs.h" - -#define IGUANA_MAXSCRIPTSIZE 10001 -#define KOMODO_KVDURATION 1440 -#define KOMODO_KVBINARY 2 -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -uint64_t komodo_paxprice(uint64_t *seedp,int32_t height,char *base,char *rel,uint64_t basevolume); -int32_t komodo_paxprices(int32_t *heights,uint64_t *prices,int32_t max,char *base,char *rel); -int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); -char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,int32_t len); -int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width); -int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen); - -UniValue kvsearch(const UniValue& params, bool fHelp) -{ - 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" - "\nArguments:\n" - "1. key (string, required) search the chain for this key\n" - "\nResult:\n" - "{\n" - " \"coin\": \"xxxxx\", (string) chain the key is stored on\n" - " \"currentheight\": xxxxx, (numeric) current height of the chain\n" - " \"key\": \"xxxxx\", (string) key\n" - " \"keylen\": xxxxx, (string) length of the key \n" - " \"owner\": \"xxxxx\" (string) hex string representing the owner of the key \n" - " \"height\": xxxxx, (numeric) height the key was stored at\n" - " \"expiration\": xxxxx, (numeric) height the key will expire\n" - " \"flags\": x (numeric) 1 if the key was created with a password; 0 otherwise.\n" - " \"value\": \"xxxxx\", (string) stored value\n" - " \"valuesize\": xxxxx (string) amount of characters stored\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("kvsearch", "examplekey") - + HelpExampleRpc("kvsearch", "\"examplekey\"") - ); - LOCK(cs_main); - 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("currentheight", (int64_t)chainActive.LastTip()->nHeight)); - 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()->nHeight,&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)); - 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")); - return ret; -} - -UniValue minerids(const UniValue& params, bool fHelp) -{ - 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 ) - height = chainActive.LastTip()->nHeight; - else - { - CBlockIndex *pblockindex = chainActive[height]; - if ( pblockindex != 0 ) - timestamp = pblockindex->GetBlockTime(); - } - 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 ) - { - for (i=0; i= numnotaries ) - tally[128]++; - else tally[minerids[i]]++; - } - for (i=0; i<64; i++) - { - 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]); - item.push_back(Pair("notaryid", i)); - - bitcoin_address(kmdaddr,60,pubkeys[i],33); - m = (int32_t)strlen(kmdaddr); - kmdaddress.resize(m); - ptr = (char *)kmdaddress.data(); - memcpy(ptr,kmdaddr,m); - item.push_back(Pair("KMDaddress", kmdaddress)); - - item.push_back(Pair("pubkey", hex)); - item.push_back(Pair("blocks", tally[i])); - a.push_back(item); - } - UniValue item(UniValue::VOBJ); - item.push_back(Pair("pubkey", (char *)"external miners")); - item.push_back(Pair("blocks", tally[128])); - a.push_back(item); - } - ret.push_back(Pair("mined", a)); - ret.push_back(Pair("numnotaries", numnotaries)); - } else ret.push_back(Pair("error", (char *)"couldnt extract minerids")); - return ret; -} - -UniValue notaries(const UniValue& params, bool fHelp) -{ - 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 ) - timestamp = (uint32_t)atol(params[1].get_str().c_str()); - else timestamp = (uint32_t)time(NULL); - if ( height < 0 ) - { - height = chainActive.LastTip()->nHeight; - timestamp = chainActive.LastTip()->GetBlockTime(); - } - else if ( params.size() < 2 ) - { - CBlockIndex *pblockindex = chainActive[height]; - if ( pblockindex != 0 ) - timestamp = pblockindex->GetBlockTime(); - } - if ( (n= komodo_notaries(pubkeys,height,timestamp)) > 0 ) - { - for (i=0; i 0 ) - ret.push_back(Pair("withdraws", opretbuf)); - else ret.push_back(Pair("withdraws", (char *)"")); - 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 ( deposited != 0 || issued != 0 || withdrawn != 0 || approved != 0 || redeemed != 0 ) - { - item.push_back(Pair("available", ValueFromAmount(available))); - item.push_back(Pair("deposited", ValueFromAmount(deposited))); - item.push_back(Pair("issued", ValueFromAmount(issued))); - 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)); - a.push_back(obj); - } - } - } - ret.push_back(Pair("fiatstatus", a)); - return ret; -} - -UniValue paxprice(const UniValue& params, bool fHelp) -{ - 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; - std::string base = params[0].get_str(); - std::string rel = params[1].get_str(); - int32_t height; - if ( params.size() == 2 ) - height = chainActive.LastTip()->nHeight; - 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); - 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); - ret.push_back(Pair("seed", seedstr)); - if ( height < 0 || height > chainActive.Height() ) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - else - { - CBlockIndex *pblockindex = chainActive[height]; - if ( pblockindex != 0 ) - ret.push_back(Pair("timestamp", (int64_t)pblockindex->nTime)); - 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; -} - -UniValue paxprices(const UniValue& params, bool fHelp) -{ - if ( fHelp || params.size() != 3 ) - throw runtime_error("paxprices \"base\" \"rel\" maxsamples\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); uint64_t relvolume,prices[4096]; uint32_t i,n; int32_t heights[sizeof(prices)/sizeof(*prices)]; - std::string base = params[0].get_str(); - std::string rel = params[1].get_str(); - int32_t maxsamples = atoi(params[2].get_str().c_str()); - if ( maxsamples < 1 ) - maxsamples = 1; - else if ( maxsamples > sizeof(heights)/sizeof(*heights) ) - maxsamples = sizeof(heights)/sizeof(*heights); - ret.push_back(Pair("base", base)); - ret.push_back(Pair("rel", rel)); - n = komodo_paxprices(heights,prices,maxsamples,(char *)base.c_str(),(char *)rel.c_str()); - UniValue a(UniValue::VARR); - for (i=0; i chainActive.Height() ) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - else - { - CBlockIndex *pblockindex = chainActive[heights[i]]; - - item.push_back(Pair("t", (int64_t)pblockindex->nTime)); - item.push_back(Pair("p", (double)prices[i] / COIN)); - a.push_back(item); - } - } - ret.push_back(Pair("array", a)); - return ret; -} - -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); - -UniValue gettxout(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 3) - throw runtime_error( - "gettxout \"txid\" n ( includemempool )\n" - "\nReturns details about an unspent transaction output.\n" - "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id\n" - "2. n (numeric, required) vout value\n" - "3. includemempool (boolean, optional) Whether to include the mempool\n" - "\nResult:\n" - "{\n" - " \"bestblock\" : \"hash\", (string) the block hash\n" - " \"confirmations\" : n, (numeric) The number of confirmations\n" - " \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"code\", (string) \n" - " \"hex\" : \"hex\", (string) \n" - " \"reqSigs\" : n, (numeric) Number of required signatures\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n" - " \"addresses\" : [ (array of string) array of Komodo addresses\n" - " \"komodoaddress\" (string) Komodo address\n" - " ,...\n" - " ]\n" - " },\n" - " \"version\" : n, (numeric) The version\n" - " \"coinbase\" : true|false (boolean) Coinbase or not\n" - "}\n" - - "\nExamples:\n" - "\nGet unspent transactions\n" - + HelpExampleCli("listunspent", "") + - "\nView the details\n" - + HelpExampleCli("gettxout", "\"txid\" 1") + - "\nAs a json rpc call\n" - + HelpExampleRpc("gettxout", "\"txid\", 1") - ); - - LOCK(cs_main); - - UniValue ret(UniValue::VOBJ); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - int n = params[1].get_int(); - bool fMempool = true; - if (params.size() > 2) - fMempool = params[2].get_bool(); - - CCoins coins; - if (fMempool) { - LOCK(mempool.cs); - CCoinsViewMemPool view(pcoinsTip, mempool); - if (!view.GetCoins(hash, coins)) - return NullUniValue; - mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool - } else { - if (!pcoinsTip->GetCoins(hash, coins)) - return NullUniValue; - } - if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) - return NullUniValue; - - BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); - CBlockIndex *pindex = it->second; - ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); - if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) - ret.push_back(Pair("confirmations", 0)); - else - { - ret.push_back(Pair("confirmations", komodo_dpowconfs(coins.nHeight,pindex->nHeight - coins.nHeight + 1))); - ret.push_back(Pair("rawconfirmations", pindex->nHeight - 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->nHeight)) != 0 ) - ret.push_back(Pair("interest", ValueFromAmount(interest))); - UniValue o(UniValue::VOBJ); - ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); - ret.push_back(Pair("scriptPubKey", o)); - ret.push_back(Pair("version", coins.nVersion)); - ret.push_back(Pair("coinbase", coins.fCoinBase)); - - return ret; -} - -UniValue verifychain(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "verifychain ( checklevel numblocks )\n" - "\nVerifies blockchain database.\n" - "\nArguments:\n" - "1. checklevel (numeric, optional, 0-4, default=3) How thorough the block verification is.\n" - "2. numblocks (numeric, optional, default=288, 0=all) The number of blocks to check.\n" - "\nResult:\n" - "true|false (boolean) Verified or not\n" - "\nExamples:\n" - + HelpExampleCli("verifychain", "") - + HelpExampleRpc("verifychain", "") - ); - - LOCK(cs_main); - - int nCheckLevel = GetArg("-checklevel", 3); - int nCheckDepth = GetArg("-checkblocks", 288); - if (params.size() > 0) - nCheckLevel = params[0].get_int(); - if (params.size() > 1) - nCheckDepth = params[1].get_int(); - - return CVerifyDB().VerifyDB(pcoinsTip, nCheckLevel, nCheckDepth); -} - -/** Implementation of IsSuperMajority with better feedback */ -static UniValue SoftForkMajorityDesc(int minVersion, CBlockIndex* pindex, int nRequired, const Consensus::Params& consensusParams) -{ - int nFound = 0; - CBlockIndex* pstart = pindex; - for (int i = 0; i < consensusParams.nMajorityWindow && pstart != NULL; i++) - { - if (pstart->nVersion >= minVersion) - ++nFound; - pstart = pstart->pprev; - } - - UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("status", nFound >= nRequired)); - rv.push_back(Pair("found", nFound)); - rv.push_back(Pair("required", nRequired)); - rv.push_back(Pair("window", consensusParams.nMajorityWindow)); - return rv; -} - -static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* pindex, const Consensus::Params& consensusParams) -{ - UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("id", name)); - rv.push_back(Pair("version", version)); - rv.push_back(Pair("enforce", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams))); - rv.push_back(Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityRejectBlockOutdated, consensusParams))); - return rv; -} - -static UniValue NetworkUpgradeDesc(const Consensus::Params& consensusParams, Consensus::UpgradeIndex idx, int height) -{ - UniValue rv(UniValue::VOBJ); - auto upgrade = NetworkUpgradeInfo[idx]; - 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; - } - rv.push_back(Pair("info", upgrade.strInfo)); - return rv; -} - -void NetworkUpgradeDescPushBack( - UniValue& networkUpgrades, - const Consensus::Params& consensusParams, - Consensus::UpgradeIndex idx, - int height) -{ - // Network upgrades with an activation height of NO_ACTIVATION_HEIGHT are - // hidden. This is used when network upgrade implementations are merged - // without specifying the activation height. - if (consensusParams.vUpgrades[idx].nActivationHeight != Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT) { - networkUpgrades.push_back(Pair( - HexInt(NetworkUpgradeInfo[idx].nBranchId), - NetworkUpgradeDesc(consensusParams, idx, height))); - } -} - - -UniValue getblockchaininfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblockchaininfo\n" - "Returns an object containing various state info regarding block chain processing.\n" - "\nNote that when the chain tip is at the last block before a network upgrade activation,\n" - "consensus.chaintip != consensus.nextblock.\n" - "\nResult:\n" - "{\n" - " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" - " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" - " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" - " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" - " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" - " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" - " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" - " \"size_on_disk\": xxxxxx, (numeric) the estimated size of the block and undo files on disk\n" - " \"commitments\": xxxxxx, (numeric) the current number of note commitments in the commitment tree\n" - " \"softforks\": [ (array) status of softforks in progress\n" - " {\n" - " \"id\": \"xxxx\", (string) name of softfork\n" - " \"version\": xx, (numeric) block version\n" - " \"enforce\": { (object) progress toward enforcing the softfork rules for new-version blocks\n" - " \"status\": xx, (boolean) true if threshold reached\n" - " \"found\": xx, (numeric) number of blocks with the new version found\n" - " \"required\": xx, (numeric) number of blocks required to trigger\n" - " \"window\": xx, (numeric) maximum size of examined window of recent blocks\n" - " },\n" - " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n" - " }, ...\n" - " ],\n" - " \"upgrades\": { (object) status of network upgrades\n" - " \"xxxx\" : { (string) branch ID of the upgrade\n" - " \"name\": \"xxxx\", (string) name of upgrade\n" - " \"activationheight\": xxxxxx, (numeric) block height of activation\n" - " \"status\": \"xxxx\", (string) status of upgrade\n" - " \"info\": \"xxxx\", (string) additional information about upgrade\n" - " }, ...\n" - " },\n" - " \"consensus\": { (object) branch IDs of the current and upcoming consensus rules\n" - " \"chaintip\": \"xxxxxxxx\", (string) branch ID used to validate the current chain tip\n" - " \"nextblock\": \"xxxxxxxx\" (string) branch ID that the next block will be validated under\n" - " }\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getblockchaininfo", "") - + HelpExampleRpc("getblockchaininfo", "") - ); - - LOCK(cs_main); - double progress; - if ( ASSETCHAINS_SYMBOL[0] == 0 ) { - progress = Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.LastTip()); - } else { - int32_t longestchain = KOMODO_LONGESTCHAIN;//komodo_longestchain(); - 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("headers", pindexBestHeader ? pindexBestHeader->nHeight : -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()->nChainWork.GetHex())); - obj.push_back(Pair("pruned", fPruneMode)); - obj.push_back(Pair("size_on_disk", CalculateCurrentUsage())); - - ZCIncrementalMerkleTree tree; - pcoinsTip->GetAnchorAt(pcoinsTip->GetBestAnchor(), tree); - #ifdef __APPLE__ - obj.push_back(Pair("commitments", (uint64_t)tree.size())); - #else - obj.push_back(Pair("commitments", tree.size())); - #endif - - CBlockIndex* tip = chainActive.LastTip(); - UniValue valuePools(UniValue::VARR); - valuePools.push_back(ValuePoolDesc("sprout", tip->nChainSproutValue, 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)); - - UniValue upgrades(UniValue::VOBJ); - for (int i = Consensus::UPGRADE_OVERWINTER; i < Consensus::MAX_NETWORK_UPGRADES; i++) { - NetworkUpgradeDescPushBack(upgrades, consensusParams, Consensus::UpgradeIndex(i), tip->nHeight); - } - obj.push_back(Pair("upgrades", upgrades)); - - UniValue consensus(UniValue::VOBJ); - consensus.push_back(Pair("chaintip", HexInt(CurrentEpochBranchId(tip->nHeight, consensusParams)))); - consensus.push_back(Pair("nextblock", HexInt(CurrentEpochBranchId(tip->nHeight + 1, consensusParams)))); - obj.push_back(Pair("consensus", consensus)); - - if (fPruneMode) - { - CBlockIndex *block = chainActive.LastTip(); - while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) - block = block->pprev; - - obj.push_back(Pair("pruneheight", block->nHeight)); - } - return obj; -} - -/** Comparison function for sorting the getchaintips heads. */ -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. */ - - if (a->nHeight != b->nHeight) - return (a->nHeight > b->nHeight); - - return a < b; - } -}; - -#include - -UniValue getchaintips(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getchaintips\n" - "Return information about all known tips in the block tree," - " including the main chain as well as orphaned branches.\n" - "\nResult:\n" - "[\n" - " {\n" - " \"height\": xxxx, (numeric) height of the chain tip\n" - " \"hash\": \"xxxx\", (string) block hash of the tip\n" - " \"branchlen\": 0 (numeric) zero for main chain\n" - " \"status\": \"active\" (string) \"active\" for the main chain\n" - " },\n" - " {\n" - " \"height\": xxxx,\n" - " \"hash\": \"xxxx\",\n" - " \"branchlen\": 1 (numeric) length of branch connecting the tip to the main chain\n" - " \"status\": \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n" - " }\n" - "]\n" - "Possible values for status:\n" - "1. \"invalid\" This branch contains at least one invalid block\n" - "2. \"headers-only\" Not all blocks for this branch are available, but the headers are valid\n" - "3. \"valid-headers\" All blocks are available for this branch, but they were never fully validated\n" - "4. \"valid-fork\" This branch is not part of the active chain, but is fully validated\n" - "5. \"active\" This is the tip of the active main chain, which is certainly valid\n" - "\nExamples:\n" - + HelpExampleCli("getchaintips", "") - + HelpExampleRpc("getchaintips", "") - ); - - 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. */ - /*static pthread_mutex_t mutex; static int32_t didinit; - if ( didinit == 0 ) - { - pthread_mutex_init(&mutex,NULL); - didinit = 1; - } - pthread_mutex_lock(&mutex);*/ - std::set setTips; - int32_t n = 0; - BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) - { - n++; - setTips.insert(item.second); - } - fprintf(stderr,"iterations getchaintips %d\n",n); - n = 0; - BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) - { - const CBlockIndex* pprev=0; - n++; - if ( item.second != 0 ) - pprev = item.second->pprev; - if (pprev) - setTips.erase(pprev); - } - fprintf(stderr,"iterations getchaintips %d\n",n); - //pthread_mutex_unlock(&mutex); - - // Always report the currently active tip. - setTips.insert(chainActive.LastTip()); - - /* Construct the output array. */ - UniValue res(UniValue::VARR); const CBlockIndex *forked; - BOOST_FOREACH(const CBlockIndex* block, setTips) - BOOST_FOREACH(const CBlockIndex* block, setTips) - { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("height", block->nHeight)); - obj.push_back(Pair("hash", block->phashBlock->GetHex())); - forked = chainActive.FindFork(block); - if ( forked != 0 ) - { - const int branchLen = block->nHeight - forked->nHeight; - 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)); - } - res.push_back(obj); - } - - return res; -} - -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())); - - return ret; -} - -UniValue getmempoolinfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getmempoolinfo\n" - "\nReturns details on the active state of the TX memory pool.\n" - "\nResult:\n" - "{\n" - " \"size\": xxxxx (numeric) Current tx count\n" - " \"bytes\": xxxxx (numeric) Sum of all tx sizes\n" - " \"usage\": xxxxx (numeric) Total memory usage for the mempool\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getmempoolinfo", "") - + HelpExampleRpc("getmempoolinfo", "") - ); - - return mempoolInfoToJSON(); -} - -UniValue invalidateblock(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "invalidateblock \"hash\"\n" - "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n" - "\nArguments:\n" - "1. hash (string, required) the hash of the block to mark as invalid\n" - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("invalidateblock", "\"blockhash\"") - + HelpExampleRpc("invalidateblock", "\"blockhash\"") - ); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - CValidationState state; - - { - LOCK(cs_main); - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlockIndex* pblockindex = mapBlockIndex[hash]; - InvalidateBlock(state, pblockindex); - } - - if (state.IsValid()) { - ActivateBestChain(state); - } - - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); - } - - return NullUniValue; -} - -UniValue reconsiderblock(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "reconsiderblock \"hash\"\n" - "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n" - "This can be used to undo the effects of invalidateblock.\n" - "\nArguments:\n" - "1. hash (string, required) the hash of the block to reconsider\n" - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("reconsiderblock", "\"blockhash\"") - + HelpExampleRpc("reconsiderblock", "\"blockhash\"") - ); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - CValidationState state; - - { - LOCK(cs_main); - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlockIndex* pblockindex = mapBlockIndex[hash]; - ReconsiderBlock(state, pblockindex); - } - - if (state.IsValid()) { - ActivateBestChain(state); - } - - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); - } - - return NullUniValue; -} diff --git a/src/script/cc.cpp b/src/script/cc.cpp index efbfb757a22..954200e7925 100644 --- a/src/script/cc.cpp +++ b/src/script/cc.cpp @@ -23,26 +23,44 @@ bool IsCryptoConditionsEnabled() } -bool IsSupportedCryptoCondition(const CC *cond) +bool IsSupportedCryptoCondition(const CC *cond, CC_SUBVER ccSubVersion) { int mask = cc_typeMask(cond); - - if (mask & ~CCEnabledTypes) return false; - + int CCEnabledTypesVersioned = CCEnabledTypes; + if (ccSubVersion >= CC_MIXED_MODE_SECHASH_SUBVER_1) CCEnabledTypesVersioned |= (1 << CC_Secp256k1hash); + if (mask & ~CCEnabledTypesVersioned) return false; + // Also require that the condition have at least one signable node - if (!(mask & CCSigningNodes)) return false; - + int CCSigningNodesVersioned = CCSigningNodes; + if (ccSubVersion >= CC_MIXED_MODE_SECHASH_SUBVER_1) CCSigningNodesVersioned = 0; // allow non signed conds + + // TODO: allow non signed conds generic evals + // if (ccSubVersion < CC_GENERIC_EVALS_SUBVER && !(mask & CCSigningNodes)) return false; + + // TODO: when generic evals are enabled + // check that eval params enabled if ccSubVersion >= CC_GENERIC_EVALS_SUBVER: + /*VerifyEval eval = [](CC* cond, void* evalcode) { + return cond->param ? 0 : 1; + }; + bool hasEvalParam = !cc_verifyEval(cond, eval, nullptr); + if (hasEvalParam && ccSubVersion < CC_GENERIC_EVALS_SUBVER) return false; */ return true; } -bool IsSignedCryptoCondition(const CC *cond) +bool IsSignedCryptoCondition(const CC *cond, CC_SUBVER ccSubVersion) { if (!cc_isFulfilled(cond)) return false; - if (1 << cc_typeId(cond) & CCSigningNodes) return true; + + // TODO enable not signed conds when generic evals enabled + // if (ccSubVersion >= CC_GENERIC_EVALS_SUBVER) return true; + + int CCSigningNodesVersioned = CCSigningNodes; + if (ccSubVersion >= CC_MIXED_MODE_SECHASH_SUBVER_1) CCSigningNodesVersioned |= (1 << CC_Secp256k1hash); // allow new secp256k1hash cond + if (1 << cc_typeId(cond) & CCSigningNodesVersioned) return true; if (cc_typeId(cond) == CC_Threshold) for (int i=0; isize; i++) - if (IsSignedCryptoCondition(cond->subconditions[i])) return true; + if (IsSignedCryptoCondition(cond->subconditions[i], ccSubVersion)) return true; return false; } @@ -62,17 +80,28 @@ CC* CCNewThreshold(int t, std::vector v) cond->size = v.size(); cond->subconditions = (CC**) calloc(v.size(), sizeof(CC*)); memcpy(cond->subconditions, v.data(), v.size() * sizeof(CC*)); + cond->dontFulfill = 0; return cond; } - +#include "utilstrencodings.h" CC* CCNewSecp256k1(CPubKey k) { CC *cond = cc_new(CC_Secp256k1); cond->publicKey = CopyPubKey(k); + cond->dontFulfill = 0; return cond; } +CC* CCNewSecp256k1Hash(CKeyID k) +{ + CC *cond = cc_new(CC_Secp256k1hash); + cond->publicKeyHash = (uint8_t*)calloc(1, k.size()); + //std::cerr << __func__ << " CKeyID=" << HexStr(k.begin(), k.begin()+k.size()) << " CKeyID.ToString=" << k.ToString() << std::endl; + memcpy(cond->publicKeyHash, k.begin(), k.size()); + cond->dontFulfill = 0; + return cond; +} CC* CCNewEval(std::vector code) { @@ -83,23 +112,34 @@ CC* CCNewEval(std::vector code) return cond; } - -CScript CCPubKey(const CC *cond, bool mixed) +// make cryptocondition ScriptPubKey +CScript CCPubKey(const CC *cond, CC_SUBVER ccSubVersion) { - unsigned char buf[1000]; size_t len; - if (mixed) - { - buf[0]='M'; - len = cc_fulfillmentBinaryMixedMode(cond, buf+1,999)+1; + unsigned char buf[MAX_FULFILLMENT_SIZE]; + size_t len; + + if (!cond) return CScript(); + + if (ccSubVersion >= CC_MIXED_MODE_SUBVER_0) { + buf[0] = (uint8_t)CC_MIXED_MODE_PREFIX + ccSubVersion; + CC *condCopy = cc_copy(cond); + // make 1st level thresholds as anon for subver 0 + // for later versions save as the mixed-mode fulfillment + if (ccSubVersion == CC_MIXED_MODE_SUBVER_0) + CCtoAnon(condCopy); + size_t maxFfilSize = (ccSubVersion == CC_MIXED_MODE_SUBVER_0 ? MAX_FULFILLMENT_SPK_SIZE_V0 : MAX_FULFILLMENT_SIZE); + len = cc_fulfillmentBinaryMixedMode(condCopy, buf+1, maxFfilSize-1) + 1; + cc_free(condCopy); } - else len = cc_conditionBinary(cond, buf); + else + len = cc_conditionBinary(cond, buf); return CScript() << std::vector(buf, buf+len) << OP_CHECKCRYPTOCONDITION; } CScript CCSig(const CC *cond) { - unsigned char buf[10000]; - size_t len = cc_fulfillmentBinary(cond, buf, 10000); + unsigned char buf[MAX_FULFILLMENT_SIZE]; + size_t len = cc_fulfillmentBinary(cond, buf, MAX_FULFILLMENT_SIZE); auto ffill = std::vector(buf, buf+len); ffill.push_back(1); // SIGHASH_ALL return CScript() << ffill; @@ -107,8 +147,8 @@ CScript CCSig(const CC *cond) std::vector CCSigVec(const CC *cond) { - unsigned char buf[10000]; - size_t len = cc_fulfillmentBinary(cond, buf, 10000); + unsigned char buf[MAX_FULFILLMENT_SIZE]; + size_t len = cc_fulfillmentBinary(cond, buf, MAX_FULFILLMENT_SIZE); auto ffill = std::vector(buf, buf+len); ffill.push_back(1); // SIGHASH_ALL return ffill; @@ -142,6 +182,21 @@ CC* CCPrune(CC *cond) return cc_readFulfillmentBinary(ffillBin.data(), ffillBin.size()-1); } +// make 1st level thresholds anonymous to have compact spks +bool CCtoAnon(const CC* cond) +{ + if (cc_typeId(cond) == CC_Threshold) { + for (int i = 0; i < cond->size; i++) { + if (cc_typeId(cond->subconditions[i]) == CC_Threshold) { + CC* saved = cond->subconditions[i]; + cond->subconditions[i] = cc_anon(saved); + cc_free(saved); + return (true); + } + } + } + return (false); +} bool GetPushData(const CScript &sig, std::vector &data) { @@ -164,15 +219,12 @@ bool GetOpReturnData(const CScript &sig, std::vector &data) } -const uint8_t CC_MIXED_MODE_PREFIX = 'M'; - - struct CC* cc_readConditionBinaryMaybeMixed(const uint8_t *condBin, size_t condBinLength) { if (condBinLength == 0) return NULL; - return condBin[0] == CC_MIXED_MODE_PREFIX ? + return CC_MixedModeSubVersion(condBin[0]) >= CC_MIXED_MODE_SUBVER_0 ? cc_readFulfillmentBinaryMixedMode(condBin+1, condBinLength-1) : cc_readConditionBinary(condBin, condBinLength); } @@ -181,14 +233,19 @@ struct CC* cc_readConditionBinaryMaybeMixed(const uint8_t *condBin, size_t condB int cc_verifyMaybeMixed(const struct CC *cond, const uint256 sigHash, const uint8_t *condBin, size_t condBinLength, VerifyEval verifyEval, void *evalContext) { - if (condBinLength == 0) return false; + if (condBinLength == 0) return 0; uint8_t condBuf[1000]; - if (condBin[0] == CC_MIXED_MODE_PREFIX) { + if (CC_MixedModeSubVersion(condBin[0]) >= CC_MIXED_MODE_SUBVER_0) { CC* condMixed = cc_readFulfillmentBinaryMixedMode(condBin+1, condBinLength-1); - if (!condMixed) return false; + if (!condMixed) return 0; condBinLength = cc_conditionBinary(condMixed, condBuf); condBin = condBuf; cc_free(condMixed); } return cc_verify(cond, sigHash.begin(), 32, 0, condBin, condBinLength, verifyEval, evalContext); } + +CC_SUBVER CC_MixedModeSubVersion(int c) +{ + return (c >= CC_MIXED_MODE_PREFIX && c <= CC_MIXED_MODE_PREFIX + CC_MIXED_MODE_SUBVER_MAX) ? (CC_SUBVER)(c - CC_MIXED_MODE_PREFIX) : CC_OLD_V1_SUBVER; +} diff --git a/src/script/cc.h b/src/script/cc.h index 985afa8f289..31c5c4a94ba 100644 --- a/src/script/cc.h +++ b/src/script/cc.h @@ -26,6 +26,20 @@ extern uint32_t ASSETCHAINS_CC; bool IsCryptoConditionsEnabled(); +const uint8_t CC_MIXED_MODE_PREFIX = 'M'; +enum CC_SUBVER : int { + CC_OLD_V1_SUBVER = -1, + CC_MIXED_MODE_SUBVER_0 = 0, + CC_MIXED_MODE_SECHASH_SUBVER_1 = 1, + CC_MIXED_MODE_SUBVER_MAX = CC_MIXED_MODE_SECHASH_SUBVER_1, +}; + + +CC_SUBVER CC_MixedModeSubVersion(int c); + +const size_t MAX_FULFILLMENT_SIZE = 10000; +const size_t MAX_FULFILLMENT_SPK_SIZE_V0 = 1000; + // Limit acceptable condition types // Prefix not enabled because no current use case, ambiguity on how to combine with secp256k1 // RSA not enabled because no current use case, not implemented @@ -33,21 +47,22 @@ const int CCEnabledTypes = 1 << CC_Secp256k1 | \ 1 << CC_Threshold | \ 1 << CC_Eval | \ 1 << CC_Preimage | \ - 1 << CC_Ed25519; + 1 << CC_Ed25519 | \ + 1 << CC_Secp256k1hash; -const int CCSigningNodes = 1 << CC_Ed25519 | 1 << CC_Secp256k1; +const int CCSigningNodes = 1 << CC_Ed25519 | 1 << CC_Secp256k1 | 1 << CC_Secp256k1hash; /* * Check if the server can accept the condition based on it's structure / types */ -bool IsSupportedCryptoCondition(const CC *cond); +bool IsSupportedCryptoCondition(const CC *cond, CC_SUBVER ccSubVersion); /* * Check if crypto condition is signed. Can only accept signed conditions. */ -bool IsSignedCryptoCondition(const CC *cond); +bool IsSignedCryptoCondition(const CC *cond, CC_SUBVER ccSubVersion); /* @@ -56,13 +71,14 @@ bool IsSignedCryptoCondition(const CC *cond); CC* CCNewPreimage(std::vector preimage); CC* CCNewEval(std::vector code); CC* CCNewSecp256k1(CPubKey k); +CC* CCNewSecp256k1Hash(CKeyID k); CC* CCNewThreshold(int t, std::vector v); /* * Turn a condition into a scriptPubKey */ -CScript CCPubKey(const CC *cond,bool mixed=false); +CScript CCPubKey(const CC *cond, CC_SUBVER ccSubVersion = CC_OLD_V1_SUBVER); /* @@ -91,6 +107,8 @@ std::string CCShowStructure(CC *cond); */ CC* CCPrune(CC *cond); +// Make first level threshold subconds anonymous +bool CCtoAnon(const CC *cond); /* * Get PUSHDATA from a script diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index d103e37ad6a..d560ff9fe95 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1373,8 +1373,10 @@ int TransactionSignatureChecker::CheckCryptoCondition( int error = cc_readFulfillmentBinaryExt((unsigned char*)ffillBin.data(), ffillBin.size()-1, &cond); if (error || !cond) return -1; - if (!IsSupportedCryptoCondition(cond)) return 0; - if (!IsSignedCryptoCondition(cond)) return 0; + CC_SUBVER ccSubVersion = CC_MixedModeSubVersion(scriptCode[0]); + + if (!IsSupportedCryptoCondition(cond, ccSubVersion)) return 0; + if (!IsSignedCryptoCondition(cond, ccSubVersion)) return 0; uint256 sighash; int nHashType = ffillBin.back(); @@ -1388,10 +1390,10 @@ int TransactionSignatureChecker::CheckCryptoCondition( return ((TransactionSignatureChecker*)checker)->CheckEvalCondition(cond); }; - //fprintf(stderr,"non-checker path\n"); + //fprintf(stderr,"%s non-checker path\n", __func__); int out = cc_verifyMaybeMixed( cond, sighash, condBin.data(), condBin.size(), eval, (void*)this); - //fprintf(stderr,"out.%d from cc_verify\n",(int32_t)out); + //fprintf(stderr,"%s out.%d from cc_verify\n", __func__, (int32_t)out); cc_free(cond); return out; } diff --git a/src/script/script.cpp b/src/script/script.cpp index b0838429a7e..6d7dcfe947a 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -355,16 +355,40 @@ bool CScript::GetOpretData(std::vector>& vData) const else return false; } +// validate mixed and non mixed-mode cc spk script opcode and data +static bool IsCCOpcodeValid(opcodetype opcode, const vector &data) +{ + if (data.size() > 0) + { + if (opcode > OP_0 && opcode < OP_PUSHDATA1) // pre mixed mode (cc_conditionBinary always < 76 bytes) or small mixed mode + return true; + else if (data[0] == CC_MIXED_MODE_PREFIX /*&& opcode > OP_0 && opcode <= OP_PUSHDATA1*/) // for mixed mode subver 'M'+0 enable longer data upto 255 b + return true; + else if (data[0] == CC_MIXED_MODE_PREFIX+1 /*&& opcode > OP_0 && opcode <= OP_PUSHDATA2*/) // for mixed mode subver >= 'M'+1 enable even longer data upto 65K b + return true; + } + return false; +} + bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript, std::vector>& vParams) const { const_iterator pc = begin(); vector data; opcodetype opcode,opcode1; - if (this->GetOp(pc, opcode, data)) - // Sha256 conditions are <76 bytes - if (data.size()>0 && (data[0]=='M' || (data[0]!='M' && opcode > OP_0 && opcode < OP_PUSHDATA1))) - //if (opcode > OP_0 && opcode < OP_PUSHDATA1) + if (this->GetOp(pc, opcode, data)) + { + // Pre mixed mode check: + // Sha256 conditions are <76 bytes (for cc pre mixed mode) + // if (opcode > OP_0 && opcode < OP_PUSHDATA1) + + // Original mixed mode condition field's length check. It actually disables long mixed mode conds for cc subversions 'M'+1 and on: + // if (data.size()>0 && (data[0]=='M' || (data[0]!='M' && opcode > OP_0 && opcode < OP_PUSHDATA1))) + + // new function that checks both non mixed and new mixed mode cc script opcode and data + if (IsCCOpcodeValid(opcode, data)) + { if (this->GetOp(pc, opcode1, data)) + { if (opcode1 == OP_CHECKCRYPTOCONDITION) { const_iterator pcCCEnd = pc; @@ -375,6 +399,9 @@ bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript, std::vectorIsPayToCryptoCondition()) return (false); if (this->GetOp(pc, opcode, data)) { - if (data[0]==CC_MIXED_MODE_PREFIX) return (true); + if (CC_MixedModeSubVersion(data[0]) >= CC_MIXED_MODE_SUBVER_0) return (true); } return (false); } @@ -412,7 +439,7 @@ const std::vector CScript::GetCCV2SPK() const if (!this->IsPayToCryptoCondition()) return (std::vector()); if (this->GetOp(pc, opcode, data)) { - if (data[0]==CC_MIXED_MODE_PREFIX) return data; + if (CC_MixedModeSubVersion(data[0]) >= CC_MIXED_MODE_SUBVER_0) return data; } return (std::vector()); } @@ -444,10 +471,13 @@ bool CScript::MayAcceptCryptoCondition() const vector data; opcodetype opcode; if (!this->GetOp(pc, opcode, data)) return false; - if (!(opcode > OP_0 && opcode < OP_PUSHDATA1)) return false; + if (!IsCCOpcodeValid(opcode, data)) return false; + CC *cond = cc_readConditionBinaryMaybeMixed(data.data(), data.size()); if (!cond) return false; - bool out = IsSupportedCryptoCondition(cond); + + CC_SUBVER ccSubVersion = CC_MixedModeSubVersion((*this)[0]); + bool out = IsSupportedCryptoCondition(cond, ccSubVersion); cc_free(cond); return out; } diff --git a/src/script/serverchecker.cpp b/src/script/serverchecker.cpp index 73c404e6d48..99bcbb6b363 100644 --- a/src/script/serverchecker.cpp +++ b/src/script/serverchecker.cpp @@ -119,13 +119,13 @@ bool ServerTransactionSignatureChecker::VerifySignature(const std::vector &condBin, ScriptError *serror) const +int ServerTransactionSignatureChecker::CheckCryptoConditionSpk(const std::vector &condBin, ScriptError *serror) const { CC* condMixed; - if (condBin[0] == CC_MIXED_MODE_PREFIX) + if (CC_MixedModeSubVersion(condBin[0]) >= CC_MIXED_MODE_SUBVER_0) { condMixed = cc_readFulfillmentBinaryMixedMode((unsigned char*)condBin.data()+1, condBin.size()-1); if (serror) *serror = SCRIPT_ERR_PUBKEYTYPE; diff --git a/src/script/serverchecker.h b/src/script/serverchecker.h index 454222a9b1b..c829381ca04 100644 --- a/src/script/serverchecker.h +++ b/src/script/serverchecker.h @@ -33,14 +33,16 @@ class ServerTransactionSignatureChecker : public TransactionSignatureChecker private: bool store; std::shared_ptr evalcodeChecker; + int64_t nTime; + int32_t nHeight; public: - ServerTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nIn, const CAmount& amount, bool storeIn, std::shared_ptr evalcodeCheckerIn, const PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nIn, amount, txdataIn), store(storeIn), evalcodeChecker(evalcodeCheckerIn) {} - ServerTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nIn, const CAmount& amount, bool storeIn) : TransactionSignatureChecker(txToIn, nIn, amount), store(storeIn) {} + ServerTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nIn, const CAmount& amount, bool storeIn, int64_t nTimeIn, int32_t nHeightIn, std::shared_ptr evalcodeCheckerIn, const PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nIn, amount, txdataIn), store(storeIn), nTime(nTimeIn), nHeight(nHeightIn), evalcodeChecker(evalcodeCheckerIn) {} + ServerTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nIn, const CAmount& amount, bool storeIn) : TransactionSignatureChecker(txToIn, nIn, amount), store(storeIn), nTime(0), nHeight(0) {} bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; int CheckEvalCondition(const CC *cond) const; - int CheckCryptoCondition(const std::vector &condBin, ScriptError *serror) const; + int CheckCryptoConditionSpk(const std::vector &condBin, ScriptError *serror) const; }; #endif // BITCOIN_SCRIPT_SERVERCHECKER_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 4cda8a61ad4..8b26e373f3a 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -28,7 +28,7 @@ #include "cc/CCinclude.h" #include "cc/eval.h" #include "key_io.h" - +#include "komodo_nSPV_defs.h" #include using namespace std; @@ -37,12 +37,6 @@ typedef vector valtype; extern uint8_t ASSETCHAINS_TXPOW; extern char NSPV_wifstr[],NSPV_pubkeystr[]; extern int32_t KOMODO_NSPV; -#ifndef KOMODO_NSPV_FULLNODE -#define KOMODO_NSPV_FULLNODE (KOMODO_NSPV <= 0) -#endif // !KOMODO_NSPV_FULLNODE -#ifndef KOMODO_NSPV_SUPERLITE -#define KOMODO_NSPV_SUPERLITE (KOMODO_NSPV > 0) -#endif // !KOMODO_NSPV_SUPERLITE uint256 SIG_TXHASH; @@ -347,25 +341,10 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP ret.clear(); vector vSolutions; + bool iscltv; - if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) - { - // if this is a CLTV script, solve for the destination after CLTV - if (scriptPubKey.IsCheckLockTimeVerify()) - { - uint8_t pushOp = scriptPubKey[0]; - uint32_t scriptStart = pushOp + 3; - - // check post CLTV script - CScript postfix = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end()); - - // check again with only postfix subscript - if (!Solver(postfix, whichTypeRet, vSolutions)) - return false; - } - else - return false; - } + if (!SolverCLTV(scriptPubKey, whichTypeRet, vSolutions, iscltv)) + return false; CKeyID keyID; switch (whichTypeRet) diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 290fb2ee7fa..fa09c676b5c 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -158,6 +158,7 @@ const char* GetTxnOutputType(txnouttype t) case TX_MULTISIG: return "multisig"; case TX_NULL_DATA: return "nulldata"; case TX_CRYPTOCONDITION: return "cryptocondition"; + case TX_CLTV: return "timelocked"; default: return "invalid"; } return NULL; @@ -213,7 +214,7 @@ static bool MatchMultisig(const CScript& script, unsigned int& required, std::ve return (it + 1 == script.end()); } - +// decodes scriptPubKey and returns the addressable part from it bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet) { vSolutionsRet.clear(); @@ -289,6 +290,25 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet, bool &isCltv) +{ + CScript scriptPubKey = _scriptPubKey; + isCltv = false; + if (scriptPubKey.IsCheckLockTimeVerify()) + { + uint8_t pushOp = scriptPubKey[0]; + uint32_t scriptStart = pushOp + 3; + + // continue with post CLTV script + scriptPubKey = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end()); + isCltv = true; + } + return Solver(scriptPubKey, typeRet, vSolutionsRet); +} + int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions) { switch (t) @@ -312,10 +332,13 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector vSolutions; - if (!Solver(scriptPubKey, whichType, vSolutions)) + CScript scriptPubKey = _scriptPubKey; + + bool iscltv = false; + if (!SolverCLTV(scriptPubKey, whichType, vSolutions, iscltv)) { //int32_t i; uint8_t *ptr = (uint8_t *)scriptPubKey.data(); //for (i=0; i scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end()); - } - - if (!Solver(scriptPubKey, whichType, vSolutions)) + bool iscltv = false; + if (!SolverCLTV(scriptPubKey, whichType, vSolutions, iscltv)) return false; + int64_t unlockTime = 0LL; + if (iscltv) + scriptPubKey.IsCheckLockTimeVerify(&unlockTime); + if (whichType == TX_PUBKEY) { CPubKey pubKey(vSolutions[0]); @@ -365,27 +384,41 @@ bool ExtractDestination(const CScript& _scriptPubKey, CTxDestination& addressRet return false; } + CTxDestination dest; if (returnPubKey) - addressRet = pubKey; + dest = pubKey; + else + dest = pubKey.GetID(); + + if (iscltv) + addressRet = CCLTVID(pubKey, unlockTime); else - addressRet = pubKey.GetID(); + addressRet = dest; return true; } else if (whichType == TX_PUBKEYHASH) { - addressRet = CKeyID(uint160(vSolutions[0])); + CTxDestination dest = CKeyID(uint160(vSolutions[0])); + if (iscltv) + addressRet = CCLTVID(boost::get(dest), unlockTime); + else + addressRet = dest; return true; } else if (whichType == TX_SCRIPTHASH) { addressRet = CScriptID(uint160(vSolutions[0])); + // we do not create CCLTVID for P2SH spks as it does not make sense + // however we do not fail it either to provide compatibility with the pre-CCLTVID code (if someone created such spks by chance) return true; } else if (IsCryptoConditionsEnabled() != 0 && whichType == TX_CRYPTOCONDITION) { - addressRet = CKeyID(uint160(vSolutions[0])); + addressRet = CCryptoConditionID(uint160(vSolutions[0])); + // we do not create CCLTVID for CC spks as it does not make sense (any timelocks should be inside the cc code) + // however we do not fail it either to provide compatibility with the pre-CCLTVID code return true; } // Multisig txns have more than one address... @@ -399,7 +432,8 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto vector vSolutions; // if this is a CLTV script, get the destinations after CLTV - if (scriptPubKey.IsCheckLockTimeVerify()) + int64_t unlockTime; + if (scriptPubKey.IsCheckLockTimeVerify(&unlockTime)) { uint8_t pushOp = scriptPubKey[0]; uint32_t scriptStart = pushOp + 3; @@ -408,7 +442,18 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto CScript postfix = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end()); // check again with only postfix subscript - return(ExtractDestinations(postfix, typeRet, addressRet, nRequiredRet)); + vector dests; + if (ExtractDestinations(postfix, typeRet, dests, nRequiredRet)) { + for (auto const &dest : dests) { + if (dest.which() == TX_PUBKEY) { + CPubKey pubKey(boost::get(dest)); + addressRet.push_back(CCLTVID(pubKey, unlockTime)); // add destinations as CLTV wrappers with unlock time + } + } + if (addressRet.empty()) + return false; + return true; + } } if (!Solver(scriptPubKey, typeRet, vSolutions)) @@ -500,6 +545,19 @@ class CScriptVisitor : public boost::static_visitor *script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; return true; } + bool operator()(const CCryptoConditionID &dest) const { + script->clear(); + return false; // can't create a cc that simple + } + // create a CLTV script: + bool operator()(const CCLTVID &dest) const { + script->clear(); + if (dest.which() == TX_PUBKEY) + *script << CScriptNum::serialize(dest.GetUnlockTime()) << OP_CHECKLOCKTIMEVERIFY << OP_DROP << ToByteVector(dest.GetPubKey()) << OP_CHECKSIG; + else + *script << CScriptNum::serialize(dest.GetUnlockTime()) << OP_CHECKLOCKTIMEVERIFY << OP_DROP << OP_DUP << OP_HASH160 << ToByteVector(dest.GetKeyID()) << OP_EQUALVERIFY << OP_CHECKSIG; + return true; + } }; } diff --git a/src/script/standard.h b/src/script/standard.h index 912c575d993..a9402f16451 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -40,6 +40,13 @@ class CScriptID : public uint160 CScriptID(const uint160& in) : uint160(in) {} }; +class CCryptoConditionID : public uint160 +{ +public: + CCryptoConditionID() : uint160() {} + CCryptoConditionID(const uint160& in) : uint160(in) {} +}; + static const unsigned int MAX_OP_RETURN_RELAY = 8192; //! bytes extern unsigned nMaxDatacarrierBytes; @@ -78,11 +85,74 @@ enum txnouttype TX_PUBKEY, TX_PUBKEYHASH, TX_SCRIPTHASH, - TX_MULTISIG, TX_CRYPTOCONDITION, + TX_CLTV, + // ^^ enum order matches types' order in CTxDestination + TX_MULTISIG, TX_NULL_DATA, }; +class CCLTVID +{ +private: + int64_t unlockTime; + txnouttype whichType; + union { + CKeyID keyid; + CPubKey pubkey; + }; + bool unlocked; +public: + CCLTVID(const CKeyID &id, int64_t ut) : keyid(id), unlockTime(ut), whichType(TX_PUBKEYHASH), unlocked(false) {} + CCLTVID(const CPubKey &pk, int64_t ut) : pubkey(pk), unlockTime(ut), whichType(TX_PUBKEY), unlocked(false) {} + + txnouttype which() const { return whichType; } + int64_t GetUnlockTime() const { return unlockTime; } + CKeyID GetKeyID() const { return keyid; } + CPubKey GetPubKey() const { return pubkey; } + CKeyID GetID() const { + if (whichType == TX_PUBKEYHASH) + return keyid; + else + return pubkey.GetID(); + } + void SetUnlocked() { unlocked = true; } + bool IsUnlocked() const { return unlocked; } + friend bool operator==(const CCLTVID &a, const CCLTVID &b) { + if (a.whichType == TX_PUBKEY && b.whichType == TX_PUBKEY) + return a.pubkey == b.pubkey && (a.unlocked == b.unlocked && !a.unlocked ? b.unlockTime == a.unlockTime : a.unlocked == b.unlocked); + else + return a.GetID() == b.GetID() && (a.unlocked == b.unlocked && !a.unlocked ? b.unlockTime == a.unlockTime : a.unlocked == b.unlocked); + } + friend bool operator<(const CCLTVID &a, const CCLTVID &b) { + if (a.whichType == TX_PUBKEY && b.whichType == TX_PUBKEY) { + if (a.pubkey < b.pubkey) + return true; + else if (a.pubkey == b.pubkey) { + if (a.unlocked == b.unlocked && !a.unlocked) + return a.unlockTime < b.unlockTime; + else + return a.unlocked < b.unlocked; + } + else + return false; + } + else { + if (a.GetID() < b.GetID()) + return true; + else if (a.GetID() == b.GetID()) { + if (a.unlocked == b.unlocked && !a.unlocked) + return a.unlockTime < b.unlockTime; + else + return a.unlocked < b.unlocked; + } + else + return false; + } + } +}; + + class CNoDestination { public: friend bool operator==(const CNoDestination &a, const CNoDestination &b) { return true; } @@ -94,15 +164,19 @@ class CNoDestination { * * CNoDestination: no destination set * * CKeyID: TX_PUBKEYHASH destination * * CScriptID: TX_SCRIPTHASH destination + * Komodo added: + * * CCryptoConditionID: TX_CRYPTOCONDITION destination + * * CCLTVID TX_CLTV destination * A CTxDestination is the internal data type encoded in a bitcoin address */ -typedef boost::variant CTxDestination; +typedef boost::variant CTxDestination; +// Verus-format for additional data added after cryptocondition as OP_DROP in the spk class COptCCParams { public: static const uint8_t VERSION_1 = 1; - static const uint8_t VERSION_2 = 2; // i needede to add version2 to allow adding pubkeys (as we violated the OptCCParams format by not adding pubkeys in MakeCCVout1,...) + static const uint8_t VERSION_2 = 2; // i needed to add version2 to allow adding pubkeys (as we violated the OptCCParams format by not adding pubkeys in MakeCCVout1,...) static bool isMyVersion(uint8_t version) { return version >= 1 && version <= 2; } uint8_t version; uint8_t evalCode; @@ -169,6 +243,7 @@ bool IsValidDestination(const CTxDestination& dest); const char* GetTxnOutputType(txnouttype t); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); +bool SolverCLTV(const CScript& _scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet, bool &isCltv); int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions); bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet, bool returnPubKey=false); @@ -177,4 +252,27 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std:: CScript GetScriptForDestination(const CTxDestination& dest); CScript GetScriptForMultisig(int nRequired, const std::vector& keys); +// helper to check if locktime from OP_CLTV opcode not more than tx lock time. Return true if nScriptLockTime <= txLockTime so the utxo spendable +inline bool TokelCheckLockTimeHelper(int64_t nScriptLockTime, int64_t txLockTime) +{ + if (!( + (txLockTime < LOCKTIME_THRESHOLD && nScriptLockTime < LOCKTIME_THRESHOLD) || + (txLockTime >= LOCKTIME_THRESHOLD && nScriptLockTime >= LOCKTIME_THRESHOLD) + )) + return false; + if (nScriptLockTime > txLockTime) + { + return false; + } + return true; +} + +// sets unlocked state in the CLTV dest +inline void TokelSetIfTimeUnlocked(CTxDestination &dest, int64_t txLockTime) +{ + CCLTVID& cltv = boost::get(dest); + if (TokelCheckLockTimeHelper(cltv.GetUnlockTime(), txLockTime)) + cltv.SetUnlocked(); +} + #endif // BITCOIN_SCRIPT_STANDARD_H diff --git a/src/sync.cpp b/src/sync.cpp index 31c3301bd25..2f17a401bad 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -157,7 +157,8 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) static void pop_lock() { dd_mutex.lock(); - (*lockstack).pop_back(); + if (lockstack.get() != NULL) + (*lockstack).pop_back(); dd_mutex.unlock(); } diff --git a/src/test-komodo-cc/test-assets.cpp b/src/test-komodo-cc/test-assets.cpp new file mode 100644 index 00000000000..13df21a8c33 --- /dev/null +++ b/src/test-komodo-cc/test-assets.cpp @@ -0,0 +1,2961 @@ + +#include +#include + +#include "cc/CCinclude.h" +#include "cc/CCupgrades.h" + +#include "cc/CCtokens.h" +#include "cc/CCtokens_impl.h" + +#include "cc/CCassets.h" +#include "cc/CCassetstx_impl.h" + +#include "cc/CCTokelData.h" + + +#include "cc/eval.h" +#include "importcoin.h" +#include "base58.h" +#include "core_io.h" +#include "key.h" +#include "main.h" +#include "primitives/transaction.h" +#include "script/cc.h" +#include "script/interpreter.h" +#include "script/serverchecker.h" +#include "txmempool.h" + +extern Eval* EVAL_TEST; +extern bool fDisableCCLogForTests; + +namespace CCAssetsTests { + +static uint8_t testNum = 0; +static const char* cctokens_test_log = "cctokens_test"; + +uint256 getRandomHash() +{ + CSHA256 sha; + uint256 res; + uint32_t rnd = rand(); + sha.Write((uint8_t*)&rnd, sizeof(rnd)); + sha.Finalize((uint8_t*)&res); + return res; +} + + +class EvalMock : public Eval +{ +public: + EvalMock() : Eval() {} + typedef std::map txns_type; + typedef std::map blocks_type; +private: + int currentHeight; + txns_type txs; + blocks_type blocks; + //std::map> spends; + +public: + bool TestIsOutputSpent(uint256 txid, int32_t v) { + + //EvalMock::txns_type::const_iterator itx = eval.getTxs().find(txid); + //if (itx != eval.getTxs().end()) { + for (auto const &tx : txs) + for(auto const vin : tx.second.vin) + if (vin.prevout.hash == txid && vin.prevout.n == v) + return true; + // } + return false; + } +private: + bool TestBasicTxRules(const CTransaction &tx) + { + CAmount inputs = 0LL; + CAmount outputs = 0LL; + + std::set> duplicates; + for(const auto &vin : tx.vin) { + CTransaction vintx; + uint256 hashBlock; + if (!GetTxUnconfirmedOpt(this, vin.prevout.hash, vintx, hashBlock)) { + std::cerr << __func__ << " can't load vintx" << std::endl; + return false; + } + if (TestIsOutputSpent(vin.prevout.hash, vin.prevout.n)) { + std::cerr << __func__ << " inputs already spent" << std::endl; + return false; + } + duplicates.insert({vin.prevout.hash, vin.prevout.n}); + inputs += vintx.vout[vin.prevout.n].nValue; + } + if (duplicates.size() != tx.vin.size()) { + std::cerr << __func__ << " duplicated inputs" << std::endl; + return false; + } + for(const auto &vout : tx.vout) { + if (vout.nValue < 0) { + std::cerr << __func__ << " vout.nValue < 0" << std::endl; + return false; + } + inputs += vout.nValue; + } + if (inputs < outputs) { + std::cerr << __func__ << " inputs < outputs" << std::endl; + return false; + } + return true; + } + + // run over CC V2 validation + bool TestRunCCEval(const CMutableTransaction &mtx) + { + CTransaction tx(mtx); + PrecomputedTransactionData txdata(tx); + ServerTransactionSignatureChecker checker(&tx, 0, 0, false, this->GetCurrentTime(), this->GetCurrentHeight(), nullptr, txdata); + CValidationState verifystate; + VerifyEval verifyEval = [] (CC *cond, void *checker) { + //fprintf(stderr,"checker.%p\n",(TransactionSignatureChecker*)checker); + return ((TransactionSignatureChecker*)checker)->CheckEvalCondition(cond); + }; + + // set some vars used in validation: + KOMODO_CONNECTING = 1; + KOMODO_CCACTIVATE = 1; + (*this).state = CValidationState(); // clear validation state + EVAL_TEST = &(*this); + + for(const auto &vin : tx.vin) { + if (IsCCInput(vin.scriptSig)) { + CC *cond = GetCryptoCondition(vin.scriptSig); + if (cond == NULL) { + LOGSTREAMFN(cctokens_test_log, CCLOG_INFO, stream << " GetCryptoCondition could not decode vin.scriptSig" << std::endl); + return false; + } + + int r = cc_verifyEval(cond, verifyEval, &checker); + if (r == 0) { + LOGSTREAMFN(cctokens_test_log, CCLOG_INFO, stream << " cc_verify error D" << std::endl); + return false; + } + //LOGSTREAMFN(cctokens_test_log, CCLOG_INFO, stream << " cc_verify okay for vin.hash=" << vin.prevout.hash.GetHex() << std::endl); + //break; + } + } + for(const auto &vout : tx.vout) { + if (vout.scriptPubKey.IsPayToCCV2()) { + + ScriptError error; + bool bCheck = checker.CheckCryptoConditionSpk(vout.scriptPubKey.GetCCV2SPK(), &error); + if (!bCheck) { + LOGSTREAMFN(cctokens_test_log, CCLOG_INFO, stream << " CheckCryptoCondition error=" << ScriptErrorString(error) << " eval=" << (*this).state.GetRejectReason() << std::endl); + return false; + } + //LOGSTREAMFN(cctokens_test_log, CCLOG_INFO, stream << " cc_verify okay for vout.nValue=" << vout.nValue << std::endl); + } + } + return true; + } +private: + bool AddTxInternal(const CTransaction &tx, bool bTryOnly) { + if (!tx.IsNull()) { + if (txs.find(tx.GetHash()) != txs.end()) { + std::cerr << __func__ << " transaction already in chain" << std::endl; + return false; + } + if (!TestBasicTxRules(tx)) return false; + if (!TestRunCCEval(tx)) return false; + if (!bTryOnly) + txs[tx.GetHash()] = tx; + return true; + } + std::cerr << __func__ << " transaction is null" << std::endl; + return false; + } +public: + bool AddTx(const CTransaction &tx) { + return AddTxInternal(tx, false); + } + bool TryAddTx(const CTransaction &tx) { + return AddTxInternal(tx, true); + } + bool AddGenesisTx(const CTransaction &tx) { + if (!tx.IsNull()) { + txs[tx.GetHash()] = tx; + return true; + } + std::cerr << __func__ << " transaction is null" << std::endl; + return false; + } + const std::map & getTxs() { return txs; } + //void SetCurrentHeight(int h) + //{ + // currentHeight = h; + //} + + virtual bool GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const + { + //std::cerr <<__func__ << " hash=" << hash.GetHex() << std::endl); + auto r = txs.find(hash); + if (r != txs.end()) { + //std::cerr <<__func__ << " hash=" << hash.GetHex() << " found" << std::endl); + txOut = r->second; + if (blocks.count(hash) > 0) + hashBlock = hash; + return true; + } + return false; + } + //virtual unsigned int GetCurrentHeight() const + //{ + // return currentHeight; + //} +}; + + +static EvalMock eval; + +static uint256 tokenid1, tokenid2, tokenid3, tokenid4, tokenidUnused; +static uint256 askid1, askid2, bidid1, bidid2, bidid3; + +// RJXkCF7mn2DRpUZ77XBNTKCe55M2rJbTcu +static CPubKey pk1 = CPubKey(ParseHex("035d3b0f2e98cf0fba19f80880ec7c08d770c6cf04aa5639bc57130d5ac54874db")); +static uint8_t privkey1[] = { 0x0d, 0xf0, 0x44, 0xc4, 0xbe, 0xd3, 0x3b, 0x74, 0xaf, 0x69, 0x6b, 0x05, 0x1d, 0xbf, 0x70, 0x14, 0x2f, 0xc3, 0xa7, 0x8d, 0xa3, 0x47, 0x38, 0xc0, 0x33, 0x6f, 0x50, 0x15, 0xe3, 0xd2, 0x85, 0xee }; +// RR2nTYFBPTJafxQ6en2dhUgaJcMDk4RWef +static CPubKey pk2 = CPubKey(ParseHex("034777b18effce6f7a849b72de8e6810bf7a7e050274b3782e1b5a13d0263a44dc")); +static uint8_t privkey2[] = { 0x9e, 0x69, 0x33, 0x27, 0x1c, 0x1c, 0x60, 0x5e, 0x57, 0xaf, 0xb6, 0xb7, 0xfd, 0xeb, 0xac, 0xa3, 0x11, 0x41, 0xd1, 0x3a, 0xe2, 0x36, 0x47, 0xfc, 0xe4, 0xe0, 0x79, 0x44, 0xae, 0xee, 0x43, 0xde }; +// RTbiYv9u1mrp7TmJspxduJCe3oarCqv9K4 +static CPubKey pk3 = CPubKey(ParseHex("025f97b6c42409e8e69eb2fdab281219aafe15169deec801ee621c63cc1ba0bb8c")); +static uint8_t privkey3[] = { 0x0d, 0xf8, 0xcd, 0xd4, 0x42, 0xbd, 0x77, 0xd2, 0xdd, 0x44, 0x89, 0x4b, 0x21, 0x78, 0xbf, 0x8d, 0x8a, 0xc3, 0x30, 0x0c, 0x5a, 0x70, 0x3d, 0xbe, 0xc9, 0x21, 0x75, 0x33, 0x23, 0x77, 0xd3, 0xde }; + +std::map testKeys = { + { pk1, privkey1 }, + { pk2, privkey2 }, + { pk3, privkey3 }, +}; + +static CPubKey pkunused = CPubKey(ParseHex("034b082c5819b5bf8798a387630ad236a8e800dbce4c4e24a46f36dfddab3cbff5")); + +static CAmount TestAddNormalInputs(CMutableTransaction &mtx, CPubKey mypk, CAmount amount) +{ + CAmount totalInputs = 0LL; + char mypkaddr[KOMODO_ADDRESS_BUFSIZE]; + Getscriptaddress(mypkaddr, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG); + for (auto const &t : eval.getTxs()) { + for (int32_t v = 0; v < t.second.vout.size(); v ++) { + char utxoaddr[KOMODO_ADDRESS_BUFSIZE]; + Getscriptaddress(utxoaddr, t.second.vout[v].scriptPubKey); + LOGSTREAMFN(cctokens_test_log, CCLOG_DEBUG1, stream << " utxoaddr=" << utxoaddr << " mypkaddr=" << mypkaddr << " tx=" << t.second.GetHash().GetHex() << " v=" << v << std::endl); + if (strcmp(utxoaddr, mypkaddr) == 0) { + uint256 txid = t.second.GetHash(); + if (!eval.TestIsOutputSpent(txid, v)) { + if (std::find_if(mtx.vin.begin(), mtx.vin.end(), [&](const CTxIn &vin) { return vin.prevout.hash == txid && vin.prevout.n == v; }) == mtx.vin.end()) + { + mtx.vin.push_back(CTxIn(txid, v)); + totalInputs += t.second.vout[v].nValue; + if (totalInputs >= amount) + return totalInputs; + } + } + } + } + } + return 0LL; +} + +// if amount == 0 returns full available inputs +static CAmount TestAddTokenInputs(CMutableTransaction &mtx, CPubKey mypk, uint256 tokenid, CAmount amount) +{ + CAmount totalInputs = 0LL; + struct CCcontract_info *cp, C; + cp = CCinit(&C, TokensV2::EvalCode()); + std::vector tokenaddrs = GetTokenV2IndexKeys(mypk); + for (auto const & tokenaddr : tokenaddrs) + { + for (auto const &t : eval.getTxs()) { + for (int32_t v = 0; v < t.second.vout.size(); v ++) { + if (t.second.vout[v].scriptPubKey.IsPayToCryptoCondition()) + { + char utxoaddr[KOMODO_ADDRESS_BUFSIZE]; + Getscriptaddress(utxoaddr, t.second.vout[v].scriptPubKey); + CTransaction tx; + LOGSTREAMFN(cctokens_test_log, CCLOG_DEBUG1, stream << " utxoaddr=" << utxoaddr << " tokenaddr=" << tokenaddr << " tx=" << t.second.GetHash().GetHex() << " v=" << v << std::endl); + if (tokenaddr == utxoaddr && + IsTokensvout(cp, &eval, t.second, v, tokenid)) { + uint256 txid = t.second.GetHash(); + if (!eval.TestIsOutputSpent(txid, v)) { + if (std::find_if(mtx.vin.begin(), mtx.vin.end(), [&](const CTxIn &vin) { return vin.prevout.hash == txid && vin.prevout.n == v; }) == mtx.vin.end()) + { + mtx.vin.push_back(CTxIn(txid, v)); + totalInputs += t.second.vout[v].nValue; + if (amount > 0 && totalInputs >= amount) + return totalInputs; + } + } + } + } + } + } + } + return totalInputs; +} + +bool TestSignTx(const CKeyStore& keystore, CMutableTransaction& mtx, int32_t vini, CAmount utxovalue, const CScript scriptPubKey) +{ + CTransaction txNewConst(mtx); + SignatureData sigdata; + auto consensusBranchId = CurrentEpochBranchId(eval.GetCurrentHeight(), Params().GetConsensus()); + if (ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, vini, utxovalue, SIGHASH_ALL), scriptPubKey, sigdata, consensusBranchId) != 0) { + UpdateTransaction(mtx, vini, sigdata); + return true; + } else { + std::cerr << __func__ << " signing error for vini=" << vini << " amount=" << utxovalue << std::endl; + return false; + } +} + +// sign normal and cc inputs like FinalizeCCV2Tx does +// signatures are not checked really in these tests +// however normal and cc vins should have valid script or condition as this is checked in the assets cc validation code +// in functions like cp->ismyvin() or TotalPubkeyNormalAmount() +bool TestFinalizeTx(CMutableTransaction& mtx, struct CCcontract_info *cp, uint8_t *myprivkey, CAmount txfee, CScript opret) +{ + auto consensusBranchId = CurrentEpochBranchId(eval.GetCurrentHeight(), Params().GetConsensus()); + CAmount totaloutputs = 0LL; + for (int i = 0; i < mtx.vout.size(); i ++) + totaloutputs += mtx.vout[i].nValue; + + if (!cp || !cp->evalcode) { + std::cerr << __func__ << " invalid cp param" << std::endl; + return false; + } + + CAmount totalinputs = 0LL; + for (int i = 0; i < mtx.vin.size(); i ++) + { + CTransaction vintx; + uint256 hashBlock; + if (i == 0 && mtx.vin[i].prevout.n == 10e8) + continue; // skip pegs vin + + if (GetTxUnconfirmedOpt(&eval, mtx.vin[i].prevout.hash, vintx, hashBlock)) { + totalinputs += vintx.vout[mtx.vin[i].prevout.n].nValue; + } else { + fprintf(stderr, "%s couldnt find vintx %s\n", __func__, mtx.vin[i].prevout.hash.ToString().c_str()); + return false; + } + } + + if (!myprivkey) { + std::cerr << __func__ << " myprivkey not set" << std::endl; + return false; + } + + CKey mykey; + mykey.SetKey32(myprivkey); + CPubKey mypk = mykey.GetPubKey(); + if (totalinputs >= totaloutputs + txfee) { + CAmount change = totalinputs - (totaloutputs + txfee); + if (change > 0) + mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + } + + if (opret.size() > 0) + mtx.vout.push_back(CTxOut(0, opret)); + + CPubKey globalpk; + char myccaddr[KOMODO_ADDRESS_BUFSIZE], + globaladdr[KOMODO_ADDRESS_BUFSIZE], + mytokenaddr[KOMODO_ADDRESS_BUFSIZE] = { '\0' }; + globalpk = GetUnspendable(cp, NULL); + _GetCCaddress(myccaddr, cp->evalcode, mypk, true); + _GetCCaddress(globaladdr, cp->evalcode, globalpk, true); + GetTokensCCaddress(cp, mytokenaddr, mypk, true); // get token or nft probe + + PrecomputedTransactionData txdata(mtx); + for (int i = 0; i < mtx.vin.size(); i ++) + { + CTransaction vintx; + uint256 hashBlock; + bool mgret; + + if (i == 0 && mtx.vin[i].prevout.n == 10e8) // skip PEGS vin + continue; + if ((mgret = GetTxUnconfirmedOpt(&eval, mtx.vin[i].prevout.hash, vintx, hashBlock)) != false) { + CCwrapper cond; + uint8_t *privkey = NULL; + + if (!vintx.vout[mtx.vin[i].prevout.n].scriptPubKey.IsPayToCryptoCondition()) + { + /*char coinaddr[KOMODO_ADDRESS_BUFSIZE]; + Getscriptaddress(coinaddr, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG); + if (strcmp(destaddr, coinaddr) != 0) { + fprintf(stderr, "%s signing error for normal vini.%d myprivkey does not match\n", __func__, i); + return false; + }*/ + + CBasicKeyStore tempKeystore; + CKey key; + key.Set(myprivkey, myprivkey+32, true); + tempKeystore.AddKey(key); + if (!TestSignTx(tempKeystore, mtx, i, vintx.vout[mtx.vin[i].prevout.n].nValue, vintx.vout[mtx.vin[i].prevout.n].scriptPubKey)) { + fprintf(stderr, "%s signing error for normal vini.%d\n", __func__, i); + return false; + } + + } else { + bool bdontsign = false; + char destaddr[KOMODO_ADDRESS_BUFSIZE]; + if (!Getscriptaddress(destaddr, vintx.vout[mtx.vin[i].prevout.n].scriptPubKey)) { + std::cerr << __func__ << " vini." << i << " could not Getscriptaddress for scriptPubKey=" << vintx.vout[mtx.vin[i].prevout.n].scriptPubKey.ToString() << std::endl; + return false; + } + + if (strcmp(destaddr, globaladdr) == 0) { + privkey = cp->CCpriv; + cond.reset(MakeCCcond1(cp->evalcode, globalpk)); + //std::cerr << __func__ << " vini." << i << " found globaladdress=" << globaladdr << " destaddr=" << destaddr << " strlen=" << strlen(globaladdr) << " evalcode=" << (int)cp->evalcode << std::endl; + } else if (strcmp(destaddr, myccaddr) == 0) { + privkey = myprivkey; + cond.reset(MakeCCcond1(cp->evalcode, mypk)); + //std::cerr << __func__ << " vini." << i << " found myccaddr=" << myccaddr << std::endl; + } else if (strcmp(destaddr, mytokenaddr) == 0) { + privkey = myprivkey; + cond.reset(MakeTokensv2CCcond1(cp->evalcode, mypk)); + //std::cerr << __func__ << " vini." << i << " found mytokenaddr=" << mytokenaddr << " evalcode=" << (int)cp->evalcode << std::endl; + } else { + const uint8_t nullpriv[32] = {'\0'}; + const uint8_t dontsign[32] = { 0xff }; + // use vector of dest addresses and conds to probe vintxconds + for (auto& t : cp->CCvintxprobes) { + char coinaddr[KOMODO_ADDRESS_BUFSIZE]; + if (t.CCwrapped.get() != NULL) { + //CCwrapper anonCond = t.CCwrapped; + //CCtoAnon(anonCond.get()); + //Getscriptaddress(coinaddr, CCPubKey(anonCond.get(), CC_MIXED_MODE_SUBVER_0)); + for (CC_SUBVER ccSubVer = CC_MIXED_MODE_SUBVER_0; ccSubVer <= CC_MIXED_MODE_SUBVER_MAX; ccSubVer = (CC_SUBVER)(ccSubVer+1)) + { + char coinaddr[KOMODO_ADDRESS_BUFSIZE]; + //Getscriptaddress(coinaddr, CCPubKey(anonCond.get(), ccSubVer)); + Getscriptaddress(coinaddr, CCPubKey(t.CCwrapped.get(), ccSubVer)); + if (strcmp(destaddr, coinaddr) == 0) { + if (memcmp(t.CCpriv, nullpriv, sizeof(t.CCpriv) / sizeof(t.CCpriv[0])) == 0) + privkey = myprivkey; + else if (memcmp(t.CCpriv, dontsign, sizeof(t.CCpriv) / sizeof(t.CCpriv[0])) == 0) + bdontsign = true; + else + privkey = t.CCpriv; + + cond = t.CCwrapped; + break; + } + } + if (cond.get() != nullptr) break; // found cond + } + } + } + if (cond.get() == NULL) { + fprintf(stderr, "%s vini.%d has CC signing error: could not find matching cond, address.(%s) %s\n", __func__, i, destaddr, EncodeHexTx(mtx).c_str()); + return false; + } + + uint256 sighash = SignatureHash(CCPubKey(cond.get()), mtx, i, SIGHASH_ALL, vintx.vout[mtx.vin[i].prevout.n].nValue, consensusBranchId, &txdata); + if (cc_signTreeSecp256k1Msg32(cond.get(), privkey, sighash.begin()) == 0 && + cc_signTreeSecp256k1HashMsg32(cond.get(), privkey, sighash.begin()) == 0) { + fprintf(stderr, "%s vini.%d has CC signing error: cc_signTreeSecp256k1Msg32 and cc_signTreeSecp256k1HashMsg32 returned error, address.(%s) %s\n", __func__, i, destaddr, EncodeHexTx(mtx).c_str()); + return false; + } + mtx.vin[i].scriptSig = CCSig(cond.get()); + } + } else { + fprintf(stderr, "%s could not find tx %s myGetTransaction returned %d\n", __func__, mtx.vin[i].prevout.hash.ToString().c_str(), mgret); + return false; + } + } + return true; +} + +// make three-eval (token+evalcode+evalcode2) 1of2 pk M-1 eval fake cryptocondition: +CC *TestMakeTokensv2CCcondMofN(uint8_t evalcode1, uint8_t evalcode2, uint8_t M, std::vector pks) +{ + // make 1of2 sigs cond + std::vector condpks; + for (auto const &pk : pks) + condpks.push_back(CCNewSecp256k1(pk)); + + std::vector thresholds; + if (evalcode1 != 0) + thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode1))); + if (evalcode1 != EVAL_TOKENSV2) // if evalCode == EVAL_TOKENSV2, it is actually MakeCCcond1of2()! + thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENSV2))); // this is eval token cc + if (evalcode2 != 0) + thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode + + thresholds.push_back(CCNewThreshold(M, condpks)); // this is 1 of 2 sigs cc + + uint8_t t = thresholds.size() > 1 ? thresholds.size() - 1 : thresholds.size(); + + return CCNewThreshold(t, thresholds); +} + +// make three-eval (token+evalcode+evalcode2) MofN cc vout: +CTxOut TestMakeTokensCCMofNvoutMixed(uint8_t evalcode1, uint8_t evalcode2, CAmount nValue, uint8_t M, const std::vector &pks, vscript_t* pvData) +{ + CTxOut vout; + CCwrapper payoutCond( TestMakeTokensv2CCcondMofN(evalcode1, evalcode2, M, pks) ); + //if (!CCtoAnon(payoutCond.get())) + // return vout; + + vout = CTxOut(nValue, CCPubKey(payoutCond.get(), CC_MIXED_MODE_SUBVER_0)); + + { + std::vector vvData; + if (pvData) + vvData.push_back(*pvData); + + COptCCParams ccp = COptCCParams(COptCCParams::VERSION_2, evalcode1, M, pks.size(), pks, vvData); // ver2 -> add pks + vout.scriptPubKey << ccp.AsVector() << OP_DROP; + } + return vout; +} + +// create mock tx with normal outputs +static CTransaction MakeNormalTx(CPubKey mypk, CAmount val) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + mtx.vin.push_back(CTxIn(getRandomHash(), 0)); + mtx.vout.push_back(CTxOut(val, GetScriptForDestination(mypk))); + return CTransaction(mtx); +} + +// token create/transfer mock tx helpers + +static CMutableTransaction MakeTokenV2CreateTx(CPubKey mypk, CAmount amount, const UniValue &utokeldata = NullUniValue) +{ + struct CCcontract_info *cp, C; + cp = CCinit(&C, TokensV2::EvalCode()); + std::string name = "Test" + std::to_string(rand()); // make tokenid unique for all tests + std::string description = "desc"; + CAmount txfee = 10000; + + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + if (TestAddNormalInputs(mtx, mypk, txfee) == 0LL) { + std::cerr << __func__ << " could not add normal inputs" << std::endl; + return CTransaction(); + } + mtx.vout.push_back(TokensV2::MakeCC1vout(TokensV2::EvalCode(), TOKENS_MARKER_VALUE, GetUnspendable(cp, NULL))); + mtx.vout.push_back(TokensV2::MakeTokensCC1vout(0, amount, mypk)); + + std::vector vextras; + vuint8_t vtokeldata; + if (!utokeldata.isNull()) + vtokeldata = ParseTokelJson(utokeldata); + if (!vtokeldata.empty()) + vextras.push_back(vtokeldata); + + if (!TestFinalizeTx(mtx, cp, testKeys[mypk], txfee, + TokensV2::EncodeTokenCreateOpRet(vscript_t(mypk.begin(), mypk.end()), name, description, vextras))) { + std::cerr << __func__ << " could finalize tx" << std::endl; + return CTransaction(); + } + return mtx; +} + +static CMutableTransaction MakeTokenV2TransferTx(const CPubKey &mypk, CAmount txfee, uint256 tokenid, const std::vector &tokenaddrs, std::vector> probeconds, uint8_t M, std::vector destinations, CAmount total, bool useMempool, bool spendMarker) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + if (total < 0) { + CCerror = strprintf("negative total"); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << CCerror << "=" << total << std::endl); + return CTransaction(); + } + + struct CCcontract_info *cp, C; + cp = CCinit(&C, TokensV2::EvalCode()); + + if (txfee == 0) + txfee = 10000; + + if (!mypk.IsFullyValid()) { + CCerror = "mypk is not set or invalid"; + return CTransaction(); + } + std::vector destpubkeys; + for (auto const &dest : destinations) + if (dest.which() == TX_PUBKEY) + destpubkeys.push_back(boost::get(dest)); + + CAmount normalInputs = TestAddNormalInputs(mtx, mypk, txfee); + if (normalInputs > 0) + { + CAmount CCchange = 0, CCinputs = 0; + for (const auto &addr : tokenaddrs) { + CAmount CCinputsOne = TestAddTokenInputs(mtx, mypk, tokenid, total); + if (CCinputsOne > 0) + CCinputs += CCinputsOne; + if (CCinputs >= total) + break; + } + + if (spendMarker) { + mtx.vin.push_back(CTxIn(tokenid, 0)); // spend tokenlist marker + } + + if (CCinputs >= total) + { + if (CCinputs > total) + CCchange = (CCinputs - total); + + if (destinations.size() == 0) { + CCerror = "no dest pubkeys"; + return CTransaction(); + } + + if (TokensV2::EvalCode() == EVAL_TOKENS) { + if (destinations.size() > 2) { + CCerror = "no more than 2 dest pubkeys supported"; + return CTransaction(); + } + } + if (TokensV2::EvalCode() == EVAL_TOKENSV2) { + if (destinations.size() > 128) { + CCerror = "no more than 128 dest pubkeys supported"; + return CTransaction(); + } + } + + mtx.vout.push_back(TokensV2::MakeTokensCCMofNDestVout(TokensV2::EvalCode(), 0, total, M, destinations)); + + // add optional custom probe conds to non-usual sign vins + for (const auto &p : probeconds) + CCAddVintxCond(cp, p.first, p.second); + + if (TokensV2::EvalCode() == EVAL_TOKENSV2) { + // probes for spending from mypk + for (const auto &mycond : GetTokenV2Conds(mypk)) + CCAddVintxCond(cp, mycond, nullptr); + + // if this is multisig - build and add multisig probes: + // find any token vin, load vin tx and extract M and pubkeys + for(int ccvin = 0; ccvin < mtx.vin.size(); ccvin ++) { + CTransaction vintx; + uint256 hashBlock; + std::vector vParams; + CScript dummy; + if (myGetTransaction(mtx.vin[ccvin].prevout.hash, vintx, hashBlock) && + vintx.vout[mtx.vin[ccvin].prevout.n].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && // get opdrop + vintx.vout[mtx.vin[ccvin].prevout.n].scriptPubKey.SpkHasEvalcodeCCV2(TokensV2::EvalCode()) && + vParams.size() > 0) + { + COptCCParams ccparams(vParams[0]); + if (ccparams.version != 0 && ccparams.vKeys.size() > 1) { + if (CCchange != 0) { + mtx.vout.push_back(TokensV2::MakeTokensCCMofNvout(TokensV2::EvalCode(), 0, CCchange, ccparams.m, ccparams.vKeys)); + CCchange = 0; + } + CCwrapper ccprobeMofN( MakeTokensv2CCcondMofN(TokensV2::EvalCode(), 0, ccparams.m, ccparams.vKeys) ); + CCAddVintxCond(cp, ccprobeMofN, nullptr); //add MofN probe to find vins and sign + break; + } + } + } + } + + if (CCchange != 0) { + if (TokensV2::EvalCode() == EVAL_TOKENSV2 && + CCUpgrades::IsUpgradeActive(eval.GetCurrentTime(), eval.GetCurrentHeight(), CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)) { + mtx.vout.push_back(TokensV2::MakeTokensCCMofNDestVout(TokensV2::EvalCode(), 0, CCchange, 1, {mypk.GetID()})); // send change to R-address after the HF + } + else + mtx.vout.push_back(TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), CCchange, mypk)); + } + + // 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 + if (!TestFinalizeTx(mtx, cp, testKeys[mypk], txfee, TokensV2::EncodeTokenOpRet(tokenid, destpubkeys, {} ))) + return CTransaction(); + return mtx; + + } + else { + if (CCinputs == 0LL) + CCerror = strprintf("no token inputs"); + else + CCerror = strprintf("insufficient token inputs"); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << CCerror << " for amount=" << total << std::endl); + } + } + else { + CCerror = strprintf("insufficient normal inputs for tx fee"); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << CCerror << std::endl); + } + return CTransaction(); +} + + +static CMutableTransaction BeginTokenV2TransferTx(const CPubKey &mypk) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CAmount txfee = 10000; + CAmount normalInputs = TestAddNormalInputs(mtx, mypk, txfee); + if (normalInputs > 0) + return mtx; + else { + LOGSTREAMFN(cctokens_test_log, CCLOG_INFO, stream << "normal inputs" << std::endl); + return CTransaction(); + } +} + +/** + * Add token outputs for tokenid to mtx + * cp CCcontract_info must be preserved until FinalizeTokenV2TransferTx call as it is to store probe conds + * mtx mtable tx + * mypk pubkey to sign inputs + * tokenid token id + * tokenaddress unused for now + * probeconds probe conds to add to cp (to match vintx scriptPubKeys) + * M min number of signers to spend the created outputs + * destpubkeys pubkeys where to send outputs + * total token amount to add + * useOpReturn add tokenid to opreturn (if true) or opdrop + * opretOut returns created opreturn if useOpReturn true + * skipInputs do not add token inputs (to create a bad token tx to test validation code) + * corruptChange change the change value (to create a bad token tx) + * splitOutputs split token amount on several outputs (to test validation code) + **/ +static bool AddTokenV2TransferOutputs(struct CCcontract_info *cp, CMutableTransaction &mtx, const CPubKey &mypk, uint256 tokenid, const char *tokenaddr, std::vector> probeconds, uint8_t M, std::vector destpubkeys, CAmount total, bool useOpReturn, CScript &opretOut, bool skipInputs = false, CAmount corruptChange = 0LL, int32_t splitOutputs = 1) +{ + CAmount CCchange = 0, inputs = 0; + if (skipInputs || (inputs = TestAddTokenInputs(mtx, mypk, tokenid, total)) >= total) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx! + { + if (inputs > total) + CCchange = (inputs - total); + + { + CScript opret = TokensV2::EncodeTokenOpRet(tokenid, destpubkeys, {}); + vscript_t vData; + GetOpReturnData(opret, vData); + CAmount partAmount = total; + if (splitOutputs > 0) { + partAmount = total / splitOutputs; + } + CAmount acc = 0LL; + for (int i = 0; i < splitOutputs; i ++) { + CAmount outputAmount = i < splitOutputs-1 ? partAmount : total - acc; // compensate round loss + LOGSTREAMFN(cctokens_test_log, CCLOG_DEBUG1, stream << " i=" << i << " outputAmount=" << outputAmount << std::endl); + std::vector vvData{ vData }; + mtx.vout.push_back(TokensV2::MakeTokensCCMofNvout(TokensV2::EvalCode(), 0, outputAmount, M, destpubkeys, useOpReturn ? nullptr : &vvData)); // add opdrop if opreturn not used + acc += outputAmount; + } + } + + // add optional custom probe conds to non-usual sign vins + for (const auto &p : probeconds) + CCAddVintxCond(cp, p.first, p.second); + + // if this is multisig - build and add multisig probes: + // find any token vin, load vin tx and extract M and pubkeys + bool ccChangeAdded = false; + for(int ccvin = 0; ccvin < mtx.vin.size(); ccvin ++) { + CTransaction vintx; + uint256 hashBlock; + std::vector vParams; + CScript dummy; + if (myGetTransaction(mtx.vin[ccvin].prevout.hash, vintx, hashBlock) && + vintx.vout[mtx.vin[ccvin].prevout.n].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && // get opdrop + vintx.vout[mtx.vin[ccvin].prevout.n].scriptPubKey.SpkHasEvalcodeCCV2(TokensV2::EvalCode()) && + vParams.size() > 0) + { + COptCCParams ccparams(vParams[0]); + if (ccparams.version != 0 && ccparams.vKeys.size() > 1) { + if (CCchange != 0 || corruptChange != 0) { + mtx.vout.push_back(TokensV2::MakeTokensCCMofNvout(TokensV2::EvalCode(), 0, CCchange + corruptChange, ccparams.m, ccparams.vKeys)); + CCchange = 0; + ccChangeAdded = true; + } + CCwrapper ccprobeMofN( MakeTokensv2CCcondMofN(TokensV2::EvalCode(), 0, ccparams.m, ccparams.vKeys) ); + CCAddVintxCond(cp, ccprobeMofN, nullptr); //add MofN probe to find vins and sign + break; + } + } + } + + if (!ccChangeAdded && (CCchange != 0 || corruptChange != 0)) + { + CScript opret = TokensV2::EncodeTokenOpRet(tokenid, {mypk}, {}); + vscript_t vData; + GetOpReturnData(opret, vData); + std::vector vvData{ vData }; + mtx.vout.push_back(TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), CCchange + corruptChange, mypk, useOpReturn ? nullptr : &vvData)); // add opdrop if opreturn not used + } + if (useOpReturn) + opretOut = TokensV2::EncodeTokenOpRet(tokenid, destpubkeys, {}); + return true; + } + else { + if (inputs == 0LL) + CCerror = strprintf("no token inputs"); + else + CCerror = strprintf("insufficient token inputs"); + LOGSTREAMFN(cctokens_test_log, CCLOG_INFO, stream << CCerror << " for amount=" << total << std::endl); + } + return false; +} + +static bool FinalizeTokenV2TransferTx(struct CCcontract_info *cp, CMutableTransaction &mtx, const CPubKey &mypk, const CScript &opret) +{ + CAmount txfee = 10000; + + if (!TestFinalizeTx(mtx, cp, testKeys[mypk], txfee, opret)) { + LOGSTREAMFN(cctokens_test_log, CCLOG_INFO, stream << " could finalize tx" << std::endl); + return false; + } + return true; +} + +static CAmount TokenV2Balance(const CPubKey &mypk, uint256 tokenid) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CAmount balance = TestAddTokenInputs(mtx, mypk, tokenid, 0); + LOGSTREAMFN(cctokens_test_log, CCLOG_DEBUG1, stream << " token v2 balance=" << balance << std::endl); + return balance; +} + +// assets tx create helpers: + +static CMutableTransaction MakeTokenV2AskTx(struct CCcontract_info *cpTokens, CPubKey mypk, uint256 tokenid, CAmount numtokens, CAmount unit_price, int32_t expiryHeight) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + + CAmount txfee = 10000; + CAmount askamount = numtokens * unit_price; + + if (TestAddNormalInputs(mtx, mypk, txfee) == 0LL) { + std::cerr << __func__ << " cant add normal inputs" << std::endl; + return CTransaction(); + } + + TokenDataTuple tokenData; + vuint8_t vextraData; + int64_t royaltyFract = 0; // royaltyFract is N in N/1000 fraction + if (!GetTokenData(&eval, tokenid, tokenData, vextraData)) { + std::cerr << __func__ << " cant get tokendata" << std::endl; + return CTransaction(); + } + + + CAmount inputs = TestAddTokenInputs(mtx, mypk, tokenid, numtokens); + if (inputs == 0) { + std::cerr << __func__ << " cant add token inputs" << std::endl; + return CTransaction(); + } + + CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, NULL); + mtx.vout.push_back(TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), numtokens, unspendableAssetsPubkey)); + mtx.vout.push_back(TokensV2::MakeCC1of2vout(AssetsV2::EvalCode(), ASSETS_MARKER_AMOUNT, mypk, unspendableAssetsPubkey)); + CAmount CCchange = inputs - numtokens; + if (CCchange != 0LL) { + // change to single-eval or non-fungible token vout (although for non-fungible token change currently is not possible) + mtx.vout.push_back(TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), CCchange, mypk)); + } + + // cond to spend NFT from mypk + CCwrapper wrCond(TokensV2::MakeTokensCCcond1(TokensV2::EvalCode(), mypk)); + CCAddVintxCond(cpTokens, wrCond, NULL); //NULL indicates to use myprivkey + + // sign vins: + if(!TestFinalizeTx(mtx, cpTokens, testKeys[mypk], txfee, + TokensV2::EncodeTokenOpRet(tokenid, { unspendableAssetsPubkey }, + { AssetsV2::EncodeAssetOpRet('s', unit_price, vuint8_t(mypk.begin(), mypk.end()), expiryHeight) }))) { + std::cerr << __func__ << " cant finalize tx" << std::endl; + return CTransaction(); + } + return mtx; +} + +static CMutableTransaction MakeTokenV2BidTx(struct CCcontract_info *cpAssets, CPubKey mypk, uint256 tokenid, CAmount numtokens, CAmount unit_price, int32_t expiryHeight, CAmount extraCoins = 0LL) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + //struct CCcontract_info *cpAssets, C; + + CAmount bidamount = numtokens * unit_price; + //CAmount numtokens = 2; + CAmount txfee = 10000; + + //cpAssets = CCinit(&C, AssetsV2::EvalCode()); // NOTE: assets here! + //CAmount unit_price = bidamount / numtokens; + + if (TestAddNormalInputs(mtx, mypk, txfee + bidamount + extraCoins) == 0LL) { + std::cerr << __func__ << " cant add normal inputs" << std::endl; + return CTransaction(); + } + CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, nullptr); + mtx.vout.push_back(TokensV2::MakeCC1vout(AssetsV2::EvalCode(), bidamount + extraCoins, unspendableAssetsPubkey)); + mtx.vout.push_back(TokensV2::MakeCC1of2vout(AssetsV2::EvalCode(), ASSETS_MARKER_AMOUNT, mypk, unspendableAssetsPubkey)); // marker for my orders + + // sign vins: + if (!TestFinalizeTx(mtx, cpAssets, testKeys[mypk], txfee, + TokensV2::EncodeTokenOpRet(tokenid, {}, + { AssetsV2::EncodeAssetOpRet('b', unit_price, vuint8_t(mypk.begin(), mypk.end()), expiryHeight) }))) { + std::cerr << __func__ << " cant finalize tx" << std::endl; + return CTransaction(); + } + return mtx; +} + +static CMutableTransaction MakeTokenV2FillAskTx(struct CCcontract_info *cpAssets, CPubKey mypk, uint256 tokenid, uint256 asktxid, CAmount fill_units, CAmount paid_unit_price, UniValue &data) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + struct CCcontract_info *cpTokens, tokensC; + + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + + CTransaction asktx; + uint256 hashBlock; + if (!GetTxUnconfirmedOpt(&eval, asktxid, asktx, hashBlock)) { + std::cerr << __func__ << " could not load asktx" << std::endl; + return CTransaction(); + } + + CAmount unit_price = 0; + // CAmount fillunits = ask; + CAmount txfee = 10000; + const int32_t askvout = ASSETS_GLOBALADDR_VOUT; + uint256 assetidOpret; + //CAmount paid_unit_price = -1; // not set + CAmount orig_assetoshis = asktx.vout[askvout].nValue; + + TokenDataTuple tokenData; + vuint8_t vextraData; + int64_t royaltyFract = 0; // royaltyFract is N in N/1000 fraction + if (!GetTokenData(&eval, tokenid, tokenData, vextraData)) { + std::cerr << __func__ << " cant get tokendata" << std::endl; + return CTransaction(); + } + bool bGetTokelDataAsInt64 = false; + if (vextraData.size() > 0) { + bGetTokelDataAsInt64 = GetTokelDataAsInt64(vextraData, TKLPROP_ROYALTY, royaltyFract); + if (royaltyFract > TKLROYALTY_DIVISOR-1) + royaltyFract = TKLROYALTY_DIVISOR-1; // royalty upper limit + } + //std::cerr << __func__ << " vextraData.size()=" << vextraData.size() << " bGetTokelDataAsInt64=" << bGetTokelDataAsInt64 << std::endl; + vuint8_t vtokenCreatorPubkey = std::get<0>(tokenData); + + vuint8_t vorigpubkey; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, asktx); // get orig pk, orig value + + if (paid_unit_price <= 0LL) + paid_unit_price = unit_price; + + CAmount paid_nValue = paid_unit_price * fill_units; + CAmount royaltyValue = royaltyFract > 0 ? paid_nValue / TKLROYALTY_DIVISOR * royaltyFract : 0; + + bool hasDust = false; + bool isRoyaltyDust = true; + if (royaltyFract > 0) { + + if (CCUpgrades::IsUpgradeActive(eval.GetCurrentTime(), eval.GetCurrentHeight(), CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1) == false) { + // corrected old calculation to allow tx creation and pass to fillask validation code + // (note division on TKLROYALTY_DIVISOR first): + if(paid_nValue - royaltyValue <= ASSETS_NORMAL_DUST / (int64_t)TKLROYALTY_DIVISOR * royaltyFract - ASSETS_NORMAL_DUST) { // if value paid to seller less than when the royalty is minimum + royaltyValue = 0; + hasDust = true; + isRoyaltyDust = true; //old code assumes dust always royalty + //std::cerr << __func__ << " old calc corrected, paid_nValue - royaltyValue=" << paid_nValue - royaltyValue << " test dust=" << ASSETS_NORMAL_DUST / (int64_t)TKLROYALTY_DIVISOR * royaltyFract - ASSETS_NORMAL_DUST << std::endl; + } + } + else { + // correct calculation: + if (AssetsFillOrderIsDust(royaltyFract, paid_nValue, isRoyaltyDust)) { + royaltyValue = 0; // all amount (with dust) to go to one pk (depending on which is not dust) + hasDust = true; + } + } + } + + + //std::cerr << __func__ << " royaltyFract=" << royaltyFract << " royaltyValue=" << royaltyValue << std::endl; + + if (TestAddNormalInputs(mtx, mypk, txfee) == 0LL) { + std::cerr << __func__ << " cant add normal inputs" << std::endl; + return CTransaction(); + } + mtx.vin.push_back(CTxIn(asktx.GetHash(), askvout, CScript())); // spend order tx + + // vout.0 tokens remainder to unspendable cc addr: + mtx.vout.push_back(TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), orig_assetoshis - fill_units, 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(TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), fill_units, mypk)); + + vuint8_t destpk = (royaltyFract > 0 && hasDust && !isRoyaltyDust) ? vtokenCreatorPubkey : vorigpubkey; // if paid_value is dust send all amount to token owner + mtx.vout.push_back(CTxOut(paid_nValue - royaltyValue, CScript() << destpk << OP_CHECKSIG)); //vout.2 coins to ask originator's normal addr + if (royaltyValue > 0) // note it makes the vout even if roaltyValue is 0 + mtx.vout.push_back(CTxOut(royaltyValue, CScript() << vtokenCreatorPubkey << OP_CHECKSIG)); // vout.3 royalty to token owner + + if (orig_assetoshis - fill_units > 0) // we dont need the marker if order is filled + mtx.vout.push_back(TokensV2::MakeCC1of2vout(AssetsV2::EvalCode(), ASSETS_MARKER_AMOUNT, pubkey2pk(vorigpubkey), GetUnspendable(cpAssets, NULL))); //vout.3(4 if royalty) marker to vorigpubkey (for my tokenorders?) + + uint8_t unspendableAssetsPrivkey[32]; + CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); + + CCwrapper wrCond1(TokensV2::MakeTokensCCcond1(AssetsV2::EvalCode(), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond1, unspendableAssetsPrivkey); + + // probe to spend marker + CCwrapper wrCond2(::MakeCCcond1of2(AssetsV2::EvalCode(), pubkey2pk(vtokenCreatorPubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond2, nullptr); // spend with mypk + + if (!TestFinalizeTx(mtx, cpAssets, testKeys[mypk], txfee, + TokensV2::EncodeTokenOpRet(tokenid, { mypk }, + { AssetsV2::EncodeAssetOpRet('S', unit_price, vorigpubkey, expiryHeight) } ))) { + std::cerr << __func__ << " cant finalize tx" << std::endl; + return CTransaction(); + } + data.pushKV("vtokenCreatorPubkey", HexStr(vtokenCreatorPubkey)); + data.pushKV("vorigpubkey", HexStr(vorigpubkey)); + data.pushKV("unit_price", unit_price); + data.pushKV("royaltyFract", royaltyFract); + data.pushKV("royaltyValue", royaltyValue); + data.pushKV("isRoyaltyDust", isRoyaltyDust); + return mtx; +} + +bool TestSetBidFillamounts(CAmount unit_price, CAmount &received_nValue, CAmount orig_nValue, CAmount &paid_units, CAmount orig_units, CAmount paid_unit_price) +{ + if (orig_units == 0) + { + received_nValue = paid_units = 0; + return(false); + } + if (paid_units > orig_units) // not + { + 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); + } + + 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) + { + CAmount remaining_nValue = (orig_nValue - received_nValue); + return true; + } + 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); + } +} + +static CMutableTransaction MakeTokenV2FillBidTx(struct CCcontract_info *cpTokens, CPubKey mypk, uint256 tokenid, uint256 bidtxid, CAmount fill_units, CAmount paid_unit_price, UniValue &data) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + struct CCcontract_info *cpAssets, C; + //struct CCcontract_info *cpTokens, tokensC; + + const int32_t bidvout = ASSETS_GLOBALADDR_VOUT; + CAmount paid_amount; + //CAmount bid_amount; + CAmount orig_units; + //CAmount unit_price = 0; + //CAmount fill_units = 2; + CAmount txfee = 10000; + uint256 assetidOpret; + + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + //cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + + CTransaction bidtx; + uint256 hashBlock; + if (!GetTxUnconfirmedOpt(&eval, bidtxid, bidtx, hashBlock)) { + std::cerr << __func__ << " could not load bidtx" << std::endl; + return CTransaction(); + } + + TokenDataTuple tokenData; + vuint8_t vextraData; + int64_t royaltyFract = 0; // royaltyFract is N in N/1000 fraction + if (!GetTokenData(&eval, tokenid, tokenData, vextraData)) { + std::cerr << __func__ << " cant get token data" << std::endl; + return CTransaction(); + } + if (vextraData.size() > 0) { + GetTokelDataAsInt64(vextraData, TKLPROP_ROYALTY, royaltyFract); + if (royaltyFract > TKLROYALTY_DIVISOR-1) + royaltyFract = TKLROYALTY_DIVISOR-1; // royalty upper limit + } + vuint8_t vtokenCreatorPubkey = std::get<0>(tokenData); + + //std::cerr << __func__ << " bidtx=" << bidtx.GetHash().GetHex() << " " << HexStr(E_MARSHAL(ss << bidtx)) << " vouts=" << bidtx.vout.size() << std::endl; + + CAmount bid_amount = bidtx.vout[bidvout].nValue; + vuint8_t vorigpubkey; + int32_t expiryHeight; + CAmount unit_price; + if (GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, bidtx) == 0) { // get orig pk, orig value + std::cerr << __func__ << " cant get order data" << std::endl; + return CTransaction(); + } + if (unit_price == 0) { + std::cerr << __func__ << " zero unit_price" << std::endl; + return CTransaction(); + } + + orig_units = bid_amount / unit_price; + + if (paid_unit_price <= 0LL) + paid_unit_price = unit_price; + + if (TestAddNormalInputs(mtx, mypk, txfee) == 0LL) { + std::cerr << __func__ << " cant add normal inputs" << std::endl; + return CTransaction(); + } + CAmount tokenInputs = TestAddTokenInputs(mtx, mypk, tokenid, fill_units); + if (tokenInputs == 0LL) { + std::cerr << __func__ << " cant add token inputs" << std::endl; + return CTransaction(); + } + //mtx.vin.push_back(CTxIn(tokenid, 1, CScript())); // spend token tx + mtx.vin.push_back(CTxIn(bidtx.GetHash(), bidvout, CScript())); // spend order tx + + if (!SetBidFillamounts(unit_price, paid_amount, bid_amount, fill_units, orig_units, paid_unit_price)) { + std::cerr << __func__ << " SetBidFillamounts returned false, continue..." << std::endl; + } + + CAmount royaltyValue = royaltyFract > 0 ? paid_amount / TKLROYALTY_DIVISOR * royaltyFract : 0; + //if (royaltyValue <= ASSETS_NORMAL_DUST) + // royaltyValue = 0LL; + bool hasDust = false; + bool isRoyaltyDust = true; + if (royaltyFract > 0) { + // correct calculation: + if (AssetsFillOrderIsDust(royaltyFract, paid_amount, isRoyaltyDust)) { + royaltyValue = 0; // all amount (with dust) to go to one pk (depending on which is not dust) + hasDust = true; + } + } + + //std::cerr << __func__ << " royaltyFract=" << royaltyFract << " royaltyValue=" << royaltyValue << " paid_amount=" << paid_amount << std::endl; + CAmount tokensChange = tokenInputs - fill_units; + + uint8_t unspendableAssetsPrivkey[32]; + CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); + + if (orig_units - fill_units > 0 || bid_amount - paid_amount <= ASSETS_NORMAL_DUST) { // bidder has coins for more tokens or only dust is sent back to global address + mtx.vout.push_back(TokensV2::MakeCC1vout(AssetsV2::EvalCode(), bid_amount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder or the dust is sent back to cc global addr + if (orig_units - fill_units == 0) + std::cerr << __func__ << " remainder dust detected, left on global addr (bid_amount - paid_amount)=" << (bid_amount - paid_amount) << std::endl; + } + else + mtx.vout.push_back(CTxOut(bid_amount - paid_amount, CScript() << vorigpubkey << OP_CHECKSIG)); // vout0 if no more tokens to buy, send the remainder to originator + mtx.vout.push_back(CTxOut(paid_amount - royaltyValue, CScript() << (royaltyFract > 0 && hasDust && !isRoyaltyDust ? vtokenCreatorPubkey : vuint8_t(mypk.begin(), mypk.end())) << OP_CHECKSIG)); // vout1 coins to mypk normal + if (royaltyValue > 0) // note it makes vout even if roaltyValue is 0 + mtx.vout.push_back(CTxOut(royaltyValue, CScript() << vtokenCreatorPubkey << OP_CHECKSIG)); // vout2 trade royalty to token owner + mtx.vout.push_back(TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), fill_units, pubkey2pk(vorigpubkey))); // vout2(3) single-eval tokens sent to the originator + //specially change to make valid tx if bidder takes tokens for lower price + //if (orig_units - fill_units > 0) // order is not finished yet + if (mtx.vout[0].scriptPubKey.IsPayToCryptoCondition() && mtx.vout[0].nValue / unit_price > 0) + mtx.vout.push_back(TokensV2::MakeCC1of2vout(AssetsV2::EvalCode(), ASSETS_MARKER_AMOUNT, pubkey2pk(vorigpubkey), unspendableAssetsPk)); // vout3(4 if royalty) marker to vorigpubkey + + if (tokensChange != 0LL) + mtx.vout.push_back(TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), tokensChange, mypk)); // change in single-eval tokens + + CMutableTransaction mtx2(mtx); // copy + + CCwrapper wrCond1(MakeCCcond1(AssetsV2::EvalCode(), unspendableAssetsPk)); // spend coins + CCAddVintxCond(cpTokens, wrCond1, unspendableAssetsPrivkey); + + CCwrapper wrCond2(TokensV2::MakeTokensCCcond1(TokensV2::EvalCode(), mypk)); // spend my tokens to fill buy + CCAddVintxCond(cpTokens, wrCond2, NULL); //NULL indicates to use myprivkey + + // probe to spend marker + CCwrapper wrCond3(::MakeCCcond1of2(AssetsV2::EvalCode(), pubkey2pk(vtokenCreatorPubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond3, nullptr); // spend with mypk + + if (!TestFinalizeTx(mtx, cpTokens, testKeys[mypk], txfee, + TokensV2::EncodeTokenOpRet(tokenid, { pubkey2pk(vorigpubkey) }, + { AssetsV2::EncodeAssetOpRet('B', unit_price, vorigpubkey, expiryHeight) }))) { + std::cerr << __func__ << " could not finalize tx" << std::endl; + return CTransaction(); + } + data.pushKV("vtokenCreatorPubkey", HexStr(vtokenCreatorPubkey)); + data.pushKV("vorigpubkey", HexStr(vorigpubkey)); + data.pushKV("unit_price", unit_price); + data.pushKV("expiryHeight", expiryHeight); + data.pushKV("royaltyFract", royaltyFract); + data.pushKV("royaltyValue", royaltyValue); + data.pushKV("isRoyaltyDust", isRoyaltyDust); + return mtx; +} + + +static CMutableTransaction MakeTokenV2CancelAskTx(struct CCcontract_info *cpAssets, CPubKey mypk, uint256 tokenid, uint256 asktxid, UniValue &data) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + CTransaction asktx; + uint256 hashBlock; + if (!GetTxUnconfirmedOpt(&eval, asktxid, asktx, hashBlock)) { + std::cerr << __func__ << " could not load asktx" << std::endl; + return CTransaction(); + } + CAmount txfee = 10000; + CAmount askamount = asktx.vout[ASSETS_GLOBALADDR_VOUT].nValue; + + TokenDataTuple tokenData; + vuint8_t vextraData; + if (!GetTokenData(&eval, tokenid, tokenData, vextraData)) { + std::cerr << __func__ << " could not load token data" << std::endl; + return CTransaction(); + } + vuint8_t vtokenCreatorPubkey = std::get<0>(tokenData); + + if (TestAddNormalInputs(mtx, mypk, txfee) == 0LL) { + std::cerr << __func__ << " could not add normal inputs" << std::endl; + return CTransaction(); + } + mtx.vin.push_back(CTxIn(asktxid, ASSETS_GLOBALADDR_VOUT, CScript())); + + uint8_t dummyEvalCode; + uint256 dummyAssetid, dummyAssetid2; + int64_t dummyPrice; + vuint8_t vorigpubkey; + int32_t expiryHeight; + uint8_t funcid = AssetsV2::DecodeAssetTokenOpRet(asktx.vout.back().scriptPubKey, dummyEvalCode, dummyAssetid, dummyPrice, vorigpubkey, expiryHeight); + if (funcid == 's' && asktx.vout.size() > 1) + mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // spend marker if funcid='s' + else if (funcid == 'S' && asktx.vout.size() > 3) + mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // spend marker if funcid='S' + else { + std::cerr << __func__ << "invalid ask tx" << std::endl; + return CTransaction(); + } + + mtx.vout.push_back(TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), askamount, vorigpubkey)); // one-eval token vout + + // init assets 'unspendable' privkey and pubkey + uint8_t unspendableAssetsPrivkey[32]; + CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); + + CCwrapper wrCond(TokensV2::MakeTokensCCcond1(AssetsV2::EvalCode(), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond, unspendableAssetsPrivkey); + + // probe to spend marker + //std::cerr << __func__ << " mypk=" << HexStr(mypk) << " vorigpubkey=" << HexStr(pubkey2pk(vorigpubkey)) << std::endl; + if (mypk == pubkey2pk(vorigpubkey)) { + CCwrapper wrCond(::MakeCCcond1of2(AssetsV2::EvalCode(), pubkey2pk(vorigpubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond, nullptr); // spend with mypk + //std::cerr << __func__ << " use mypk" << std::endl; + } else { + CCwrapper wrCond(::MakeCCcond1of2(AssetsV2::EvalCode(), pubkey2pk(vorigpubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond, unspendableAssetsPrivkey); // spend with shared pk + //std::cerr << __func__ << " use unspendableAssetsPrivkey=" << HexStr(unspendableAssetsPrivkey, unspendableAssetsPrivkey+32) << std::endl; + } + + if (!TestFinalizeTx(mtx, cpAssets, testKeys[mypk], txfee, + TokensV2::EncodeTokenOpRet(tokenid, { vorigpubkey }, + { AssetsV2::EncodeAssetOpRet('x', 0, vuint8_t(), 0) } ))) { + std::cerr << __func__ << " could not finalize tx" << std::endl; + return CTransaction(); + } + + data.pushKV("vtokenCreatorPubkey", HexStr(vtokenCreatorPubkey)); + data.pushKV("vorigpubkey", HexStr(vorigpubkey)); + data.pushKV("askamount", askamount); + + return mtx; +} + +static CMutableTransaction MakeTokenV2CancelBidTx(struct CCcontract_info *cpAssets, CPubKey mypk, uint256 tokenid, uint256 bidtxid, UniValue &data) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + CTransaction bidtx; + uint256 hashBlock; + if (!GetTxUnconfirmedOpt(&eval, bidtxid, bidtx, hashBlock)) { + std::cerr << __func__ << " could not load bidtx" << std::endl; + return CTransaction(); + } + CAmount txfee = 10000; + CAmount bidamount = bidtx.vout[ASSETS_GLOBALADDR_VOUT].nValue; + + TokenDataTuple tokenData; + vuint8_t vextraData; + if (!GetTokenData(&eval, tokenid, tokenData, vextraData)) { + std::cerr << __func__ << " could not load token data" << std::endl; + return CTransaction(); + } + vuint8_t vtokenCreatorPubkey = std::get<0>(tokenData); + + if (TestAddNormalInputs(mtx, mypk, txfee) == 0LL) { + std::cerr << __func__ << " could not add normal inputs" << std::endl; + return CTransaction(); + } + mtx.vin.push_back(CTxIn(bidtxid, ASSETS_GLOBALADDR_VOUT, CScript())); + + uint8_t dummyEvalCode; + uint256 dummyAssetid, dummyAssetid2; + int64_t dummyPrice; + vuint8_t vorigpubkey; + int32_t expiryHeight; + uint8_t funcid = AssetsV2::DecodeAssetTokenOpRet(bidtx.vout.back().scriptPubKey, dummyEvalCode, dummyAssetid, dummyPrice, vorigpubkey, expiryHeight); + if (funcid == 'b' && bidtx.vout.size() > 1) + mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b' + else if (funcid == 'B' && bidtx.vout.size() > 3) + mtx.vin.push_back(CTxIn(bidtxid, 3, CScript())); // spend marker if funcid='B' + else { + std::cerr << __func__ << "invalid bid tx" << std::endl; + return CTransaction(); } + + if (bidamount > ASSETS_NORMAL_DUST) + mtx.vout.push_back(CTxOut(bidamount, CScript() << vorigpubkey << OP_CHECKSIG)); + else { + // send dust back to global addr + mtx.vout.push_back(TokensV2::MakeCC1vout(AssetsV2::EvalCode(), bidamount, GetUnspendable(cpAssets, NULL))); + std::cerr << __func__ << " remainder dust detected, left on global addr, bidamount=" << bidamount << std::endl; + } + + // init assets 'unspendable' privkey and pubkey + uint8_t unspendableAssetsPrivkey[32]; + CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); + + // probe to spend marker + if (mypk == pubkey2pk(vorigpubkey)) { + CCwrapper wrCond(::MakeCCcond1of2(AssetsV2::EvalCode(), pubkey2pk(vorigpubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond, nullptr); // spend with mypk + } else { + CCwrapper wrCond(::MakeCCcond1of2(AssetsV2::EvalCode(), pubkey2pk(vorigpubkey), unspendableAssetsPk)); + CCAddVintxCond(cpAssets, wrCond, unspendableAssetsPrivkey); // spend with shared pk + } + + // sign, add change, add opreturn + if (!TestFinalizeTx(mtx, cpAssets, testKeys[mypk], txfee, + TokensV2::EncodeTokenOpRet(tokenid, { vorigpubkey }, + { AssetsV2::EncodeAssetOpRet('o', 0, vuint8_t(), 0) } ))) { + std::cerr << __func__ << " could not finalise tx" << std::endl; + return CTransaction(); + } + data.pushKV("vtokenCreatorPubkey", HexStr(vtokenCreatorPubkey)); + data.pushKV("vorigpubkey", HexStr(vorigpubkey)); + data.pushKV("bidamount", bidamount); + return mtx; +} + +static void CreateMockTransactions() +{ + struct CCcontract_info *cpTokens, tokensC; + struct CCcontract_info *cpAssets, C; + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + + CTransaction txnormal1 = MakeNormalTx(pk1, 200000000); + eval.AddGenesisTx(txnormal1); + + CTransaction txnormal2 = MakeNormalTx(pk1, 200000000); + eval.AddGenesisTx(txnormal2); + + CTransaction txnormal3 = MakeNormalTx(pk2, 200000000); + eval.AddGenesisTx(txnormal3); + + CTransaction txnormal4 = MakeNormalTx(pk2, 200000000); + eval.AddGenesisTx(txnormal4); + + CTransaction txnormal5 = MakeNormalTx(pk2, 200000000); + eval.AddGenesisTx(txnormal5); + + CTransaction txnormalg = MakeNormalTx(GetUnspendable(cpTokens, NULL), 200000000); + eval.AddGenesisTx(txnormalg); + + CTransaction txtokencreate1 = MakeTokenV2CreateTx(pk1, 100); + ASSERT_TRUE(eval.AddTx(txtokencreate1)); + tokenid1 = txtokencreate1.GetHash(); + + CTransaction txtokencreate2 = MakeTokenV2CreateTx(pk2, 10); + eval.AddTx(txtokencreate2); + tokenid2 = txtokencreate2.GetHash(); + + CTransaction txtokencreate3 = MakeTokenV2CreateTx(pk2, 10); + eval.AddTx(txtokencreate3); + tokenid3 = txtokencreate3.GetHash(); + + CTransaction txtokencreate4 = MakeTokenV2CreateTx(pk1, 10); + eval.AddTx(txtokencreate4); + tokenid4 = txtokencreate4.GetHash(); + + CTransaction txtokencreateUnused = MakeTokenV2CreateTx(pk1, 10); + eval.AddTx(txtokencreateUnused); + tokenidUnused = txtokencreateUnused.GetHash(); + + CTransaction txask1 = MakeTokenV2AskTx(cpTokens, pk1, tokenid1, 2, ASSETS_NORMAL_DUST+1, 222); + eval.AddTx(txask1); + askid1 = txask1.GetHash(); + + CTransaction txask2 = MakeTokenV2AskTx(cpTokens, pk1, tokenid1, 2, ASSETS_NORMAL_DUST+1, 222); + eval.AddTx(txask2); + askid2 = txask2.GetHash(); + + CTransaction txbid1 = MakeTokenV2BidTx(cpAssets, pk2, tokenid2, 2, ASSETS_NORMAL_DUST+1, 222); + eval.AddTx(txbid1); + bidid1 = txbid1.GetHash(); + + CTransaction txbid2 = MakeTokenV2BidTx(cpAssets, pk2, tokenid2, 2, ASSETS_NORMAL_DUST+2, 222, ASSETS_NORMAL_DUST+1); + eval.AddTx(txbid2); + bidid2 = txbid2.GetHash(); + + CTransaction txbid3 = MakeTokenV2BidTx(cpAssets, pk2, tokenid2, 2, ASSETS_NORMAL_DUST+1, 222, ASSETS_NORMAL_DUST-1); + eval.AddTx(txbid3); + bidid3 = txbid3.GetHash(); + + //CTransaction txbid2 = MakeTokenV2BidTx(pk2, 1000+1, 2, 1000/2, 222); // test dust +} + +// test setup +class TestAssetsCC : public ::testing::Test { +public: + //uint32_t GetAssetchainsCC() const { return testCcid; } + //std::string GetAssetchainsSymbol() const { return testSymbol; } + +protected: + static void SetUpTestCase() { + // setup eval for tests + ASSETCHAINS_CC = 2; + fDisableCCLogForTests = true; // disable LOGSTREAM prints, enable for tests debug + fDebug = false; + fPrintToConsole = true; // enable print to console + mapMultiArgs["-debug"] = { "cctokens", "ccassets", "cctokens_test" }; + + strcpy(ASSETCHAINS_SYMBOL, ""); + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); // select default + eval.SetCurrentHeight(11); // base height for tests + eval.SetCurrentTime(1652932911LL); // base block time for tests + CreateMockTransactions(); + } + + // called on each test startup + virtual void SetUp() { + // enable print + //fDebug = true; + //fPrintToConsole = true; + //mapMultiArgs["-debug"] = { "cctokens", "ccassets", "cctokens_test" }; + strcpy(ASSETCHAINS_SYMBOL, ""); + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); // select default + } + + // called on each test finish + virtual void TearDown() + { + // clean up + } +}; + +// -------------------------------------------------- +// assets cc tests: + +TEST_F(TestAssetsCC, tokenv2ask_basic) +{ + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + + eval.SetCurrentHeight(111); //set height + + CAmount numtokens = 2LL; + CPubKey mypk = pk1; + uint256 mytokenid = tokenid1; + + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); // NOTE: assets here! + CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, 0); + + CMutableTransaction mtx = MakeTokenV2AskTx(cpTokens, mypk, mytokenid, numtokens, 501, 222); // price more than dust + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, mtx); + + // test: valid tokenv2ask + EXPECT_TRUE(eval.TryAddTx(mtx)); + + { + // test: invalid unit_price == 0 + CMutableTransaction mtx1(mtx); + mtx1.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[mypk], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { unspendableAssetsPubkey }, + { AssetsV2::EncodeAssetOpRet('s', 0, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), expiryHeight) }))); + EXPECT_FALSE(eval.TryAddTx(mtx1)); + } + { + // test: invalid token dest pubkey (not global) + CMutableTransaction mtx1(mtx); + mtx1.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[mypk], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { pkunused }, // bad pk instead of global + { AssetsV2::EncodeAssetOpRet('s', unit_price, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), expiryHeight) }))); + EXPECT_TRUE(eval.TryAddTx(mtx1)); // == true as pk in token opreturn not used in tokens v2 + } + { + // test: bad origpk in opreturn (not the tx signer) + CMutableTransaction mtx1(mtx); + mtx1.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[mypk], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { unspendableAssetsPubkey }, + { AssetsV2::EncodeAssetOpRet('s', unit_price, vuint8_t(pkunused.begin(), pkunused.end()), expiryHeight) }))); // not matched origpk (should be pk1) + EXPECT_FALSE(eval.TryAddTx(mtx1)); + } + { + // test: only one opreturn supported + CMutableTransaction mtx1(mtx); + // mtx1.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[mypk], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { unspendableAssetsPubkey }, + { AssetsV2::EncodeAssetOpRet('s', unit_price, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), expiryHeight) }))); + EXPECT_FALSE(eval.TryAddTx(mtx1)); + } + { + // test: send to non-global assets destination + CMutableTransaction mtx1(mtx); + mtx1.vout[0] = TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), numtokens, pkunused); + mtx1.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[mypk], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { unspendableAssetsPubkey }, + { AssetsV2::EncodeAssetOpRet('s', unit_price, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), expiryHeight) }))); + EXPECT_FALSE(eval.TryAddTx(mtx1)); // == true as pk in token opreturn not used in tokens v2 + } + { + // test: add extra global addr vout + CMutableTransaction mtx1(mtx); + // add two vouts with numtokens/2 (to have token balance correct). Assets cc should fail however: + mtx1.vout[0] = TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), numtokens/2, unspendableAssetsPubkey); + mtx1.vout.insert(mtx1.vout.begin(), TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), numtokens/2, unspendableAssetsPubkey)); + mtx1.vout.pop_back(); // remove old opreturn + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[mypk], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { unspendableAssetsPubkey }, + { AssetsV2::EncodeAssetOpRet('s', unit_price, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), expiryHeight) }))); + EXPECT_FALSE(eval.TryAddTx(mtx1)); // == true as pk in token opreturn not used in tokens v2 + } + { + // test: add extra global addr null vout + CMutableTransaction mtx1(mtx); + // add two vouts with numtokens/2 (to have token balance correct). Assets cc should fail however: + mtx1.vout[0] = TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), numtokens, unspendableAssetsPubkey); + mtx1.vout.insert(mtx1.vout.begin(), TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), 0, unspendableAssetsPubkey)); + mtx1.vout.pop_back(); // remove old opreturn + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[mypk], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { unspendableAssetsPubkey }, + { AssetsV2::EncodeAssetOpRet('s', unit_price, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), expiryHeight) }))); + EXPECT_FALSE(eval.TryAddTx(mtx1)); // == true as pk in token opreturn not used in tokens v2 + } + { + // test: add extra marker + CMutableTransaction mtx1(mtx); + ASSERT_TRUE(mtx.vout.size() > 2); + mtx1.vout.insert(mtx1.vout.begin()+1, mtx1.vout[1]); // copy marker + mtx1.vout.pop_back(); // remove old opreturn + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[mypk], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { unspendableAssetsPubkey }, + { AssetsV2::EncodeAssetOpRet('s', unit_price, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), expiryHeight) }))); + EXPECT_FALSE(eval.TryAddTx(mtx1)); // == true as pk in token opreturn not used in tokens v2 + } + { + // test: different tokenid in opdrop + eval.SetCurrentHeight(CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT); + strcpy(ASSETCHAINS_SYMBOL, "TOKEL"); + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); + + CAmount unit_price = 501; + CMutableTransaction mtx = MakeTokenV2AskTx(cpTokens, mypk, tokenid1, numtokens, unit_price, 222); // price more than dust + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + CScript opret = TokensV2::EncodeTokenOpRet(tokenid1, { unspendableAssetsPubkey }, {}); + vscript_t vopret; + GetOpReturnData(opret, vopret); + std::vector vData { vopret }; + + CScript opretCh = TokensV2::EncodeTokenOpRet(tokenid1, { mypk }, {}); + vscript_t vopretCh; + GetOpReturnData(opretCh, vopretCh); + std::vector vDataCh { vopretCh }; + + mtx.vout[0] = TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), mtx.vout[0].nValue, unspendableAssetsPubkey, &vData); + mtx.vout[2] = TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), mtx.vout[2].nValue, mypk, &vDataCh); // cc change + mtx.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx, cpTokens, testKeys[mypk], 10000, + TokensV2::EncodeTokenOpRet(tokenid2, { unspendableAssetsPubkey }, + { AssetsV2::EncodeAssetOpRet('s', unit_price, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), expiryHeight) }))); + //std::cerr << __func__ << " tokenv2ask_basic different tokenid in opdrop.." << " GetRejectReason=" << eval.state.GetRejectReason() << std::endl; + EXPECT_TRUE(!eval.TryAddTx(mtx) && eval.state.GetRejectReason().find("invalid tokenid") != std::string::npos); // fail: can't ask for different tokenid + } +} + +TEST_F(TestAssetsCC, tokenv2bid_basic) +{ + CAmount txfee = 10000; + eval.SetCurrentHeight(111); //set height + + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); // NOTE: assets here! + CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, 0); + + CMutableTransaction mtx = MakeTokenV2BidTx(cpAssets, pk2, tokenid1, 2, 501, 222); // price more than dust + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: valid tokenv2bid + EXPECT_TRUE(eval.TryAddTx(mtx)); + + { + CMutableTransaction mtx1(mtx); + + // test: too low bid amount < unit_price + mtx1.vout[0] = TokensV2::MakeCC1vout(AssetsV2::EvalCode(), 9999, unspendableAssetsPubkey); + mtx1.vout.pop_back(); + + ASSERT_TRUE(TestFinalizeTx(mtx1, cpAssets, testKeys[pk2], txfee, + TokensV2::EncodeTokenOpRet(tokenid1, {}, + { AssetsV2::EncodeAssetOpRet('b', 10000, vuint8_t(pk2.begin(), pk2.end()), 0) }))); + EXPECT_FALSE(eval.TryAddTx(mtx1)); // must fail + } +} + + +TEST_F(TestAssetsCC, tokenv2fillask_basic) +{ + UniValue data(UniValue::VOBJ); + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, 0); + + eval.SetCurrentHeight(111); //set height + + for (CAmount fillUnits : { 1, 2 }) { // fill partially and totally + CMutableTransaction mtx = MakeTokenV2FillAskTx(cpAssets, pk2, tokenid1, askid1, fillUnits, 0, data); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: valid tokenv2fillask + EXPECT_TRUE(eval.TryAddTx(mtx)); + } + + CTransaction txask1; + CTransaction txbid1; + CTransaction txask2; + + uint256 hashBlock; + ASSERT_TRUE(GetTxUnconfirmedOpt(&eval, askid1, txask1, hashBlock)); + ASSERT_TRUE(GetTxUnconfirmedOpt(&eval, bidid1, txbid1, hashBlock)); + ASSERT_TRUE(GetTxUnconfirmedOpt(&eval, askid2, txask2, hashBlock)); + + //vuint8_t vtokenCreatorPubkey = ParseHex(data["vtokenCreatorPubkey"].getValStr()); + CMutableTransaction mtx = MakeTokenV2FillAskTx(cpAssets, pk2, tokenid1, askid1, 2, 0, data); + + // test: spend invalid tokenid + { + CMutableTransaction mtx1(mtx); + mtx1.vin.back() = CTxIn(askid1, ASSETS_GLOBALADDR_VOUT, CScript()); // spend order tx + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + CTransaction txask1; + uint256 hashBlock; + ASSERT_TRUE(GetTxUnconfirmedOpt(&eval, askid1, txask1, hashBlock)); + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txask1); + ASSERT_TRUE(funcid != 0); + mtx1.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx1, cpAssets, testKeys[pk2], 10000, + TokensV2::EncodeTokenOpRet(tokenidUnused, { pk2 }, + { AssetsV2::EncodeAssetOpRet('S', unit_price, vorigpubkey, expiryHeight) } ))); + + EXPECT_FALSE(eval.TryAddTx(mtx1)); // must fail + } + { + // test: use opposite funcid + CMutableTransaction mtx1(mtx); + + CAmount txfee = 10000; + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txbid1); + + mtx1.vout.pop_back(); // remove opret to replace it in TestFinalizeTx + ASSERT_TRUE(TestFinalizeTx(mtx1, cpAssets, testKeys[pk2], txfee, + TokensV2::EncodeTokenOpRet(assetidOpret, { pubkey2pk(vorigpubkey) }, + { AssetsV2::EncodeAssetOpRet('B', unit_price, vorigpubkey, expiryHeight) }))); // 'S' -> 'B' + + EXPECT_FALSE(eval.TryAddTx(mtx1)); // must fail: incorrect funcid + } + // test: spend yet another order + { + CMutableTransaction mtx2(mtx); + mtx2.vin.push_back( CTxIn(askid2, ASSETS_GLOBALADDR_VOUT, CScript()) ); // spend yet another order tx + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txask2); + ASSERT_TRUE(funcid != 0); + mtx2.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx2, cpAssets, testKeys[pk2], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { pk2 }, + { AssetsV2::EncodeAssetOpRet('S', unit_price, vorigpubkey, expiryHeight) } ))); + + EXPECT_FALSE(eval.TryAddTx(mtx2)); // must fail + } + { + // test: change unit_price in opret + CMutableTransaction mtx2(mtx); + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + CTransaction txask1; + uint256 hashBlock; + ASSERT_TRUE(GetTxUnconfirmedOpt(&eval, askid1, txask1, hashBlock)); + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txask1); // get orig pk, orig value + ASSERT_TRUE(funcid != 0); + mtx2.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx2, cpAssets, testKeys[pk2], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { pk2 }, + { AssetsV2::EncodeAssetOpRet('S', unit_price+1, vorigpubkey, expiryHeight) } ))); + + EXPECT_FALSE(eval.TryAddTx(mtx2)); // must fail + } + { + // test: changed origpk in assets opreturn + CMutableTransaction mtx1(mtx); + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txask1); // get orig pk, orig value + mtx1.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx1, cpAssets, testKeys[pk2], 10000, + TokensV2::EncodeTokenOpRet(tokenid1, { pk2 }, + { AssetsV2::EncodeAssetOpRet('S', unit_price, vuint8_t(pkunused.begin(), pkunused.end()), expiryHeight) }))); // not matched origpk (should be pk1) + EXPECT_FALSE(eval.TryAddTx(mtx1)); + } + { + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txask1); + + CMutableTransaction mtx = MakeTokenV2FillAskTx(cpAssets, pk2, tokenid1, askid1, 2, unit_price-1, data); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: invalid tokenv2fillask with a lower price + EXPECT_FALSE(eval.TryAddTx(mtx)); // must fail + } + { + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txask1); + + CMutableTransaction mtx = MakeTokenV2FillAskTx(cpAssets, pk2, tokenid1, askid1, 2, unit_price+1, data); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: valid tokenv2fillask with a bigger price + EXPECT_TRUE(eval.TryAddTx(mtx)); + } + { + for (CAmount fillUnits : { 1, 2 }) { // fill partially and totally + // test: fillask with different tokenid in opdrop + eval.SetCurrentHeight(CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT); + //eval.SetCurrentHeight(1); + + strcpy(ASSETCHAINS_SYMBOL, "TOKEL"); + //strcpy(ASSETCHAINS_SYMBOL, "MYCHAIN_8173645"); + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); + + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + CTransaction txask1; + uint256 hashBlock; + ASSERT_TRUE(GetTxUnconfirmedOpt(&eval, askid1, txask1, hashBlock)); + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txask1); + + CMutableTransaction mtx2 = MakeTokenV2FillAskTx(cpAssets, pk2, assetidOpret, askid1, fillUnits, unit_price, data); + + CAmount txfee = 10000; + CAmount otherAmount = TestAddTokenInputs(mtx2, pk2, tokenid3, mtx2.vout[0].nValue); //add tokenid3 + ASSERT_TRUE(otherAmount > 0); + mtx2.vout.insert(mtx2.vout.begin() + mtx2.vout.size() - 1, mtx2.vout[0]); // copy vout with tokens with assetidOpret back + { + CScript opret = TokensV2::EncodeTokenOpRet(tokenid3, { unspendableAssetsPubkey }, {}); // put tokenid3 in opdrop while assetid = tokenid2 + vscript_t vopret; + GetOpReturnData(opret, vopret); + std::vector vData { vopret }; + mtx2.vout[0] = TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), mtx2.vout[0].nValue, unspendableAssetsPubkey, &vData); // replace remainder with tokenid3 + } + + if (otherAmount > mtx2.vout[0].nValue) { // if tokenid3 change exists + CScript opret = TokensV2::EncodeTokenOpRet(tokenid3, { pk2 }, {}); + vscript_t vopret; + GetOpReturnData(opret, vopret); + std::vector vData { vopret }; + mtx2.vout.insert(mtx2.vout.begin() + mtx2.vout.size() - 1, + TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), otherAmount - mtx2.vout[0].nValue, pk2, &vData)); // add tokenid3 change + } + + mtx2.vout.pop_back(); // remove opret to replace it in TestFinalizeTx + CCwrapper wrCond1(TokensV2::MakeTokensCCcond1(TokensV2::EvalCode(), pk2)); // spend my tokens to fill buy + CCAddVintxCond(cpAssets, wrCond1, NULL); //NULL indicates to use myprivkey + + ASSERT_TRUE(TestFinalizeTx(mtx2, cpAssets, testKeys[pk2], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { pk2 }, + { AssetsV2::EncodeAssetOpRet('S', unit_price, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), expiryHeight) }))); + + //std::cerr << __func__ << " test: tokenfillask with different tokenid in opdrop, fillUnuts=" << fillUnits << std::endl; + EXPECT_FALSE(assetidOpret == tokenid3); + EXPECT_TRUE(!eval.TryAddTx(mtx2) && eval.state.GetRejectReason().find("invalid tokenid") != std::string::npos); // must fail: can't fill with another tokenid in opdrop + } + } + + { + // test: make different tokenid in opdrop in ask and try fillask it + eval.SetCurrentHeight(111); //set height + //eval.SetCurrentHeight(CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT); + strcpy(ASSETCHAINS_SYMBOL, "TOKEL"); + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); + + CAmount numtokens = 2LL; + CPubKey mypk = pk1; + uint256 mytokenid = tokenid1; + CAmount unit_price = 501; + int32_t expiryHeight = CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT+222; + + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + CMutableTransaction mtx = MakeTokenV2AskTx(cpTokens, mypk, tokenid1, numtokens, unit_price, expiryHeight); // price more than dust + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + { + CScript opret = TokensV2::EncodeTokenOpRet(tokenid1, { unspendableAssetsPubkey }, {}); + vscript_t vopret; + GetOpReturnData(opret, vopret); + std::vector vData { vopret }; + mtx.vout[0] = TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), mtx.vout[0].nValue, unspendableAssetsPubkey, &vData); + } + { + CScript opretCh = TokensV2::EncodeTokenOpRet(tokenid1, { mypk }, {}); + vscript_t vopretCh; + GetOpReturnData(opretCh, vopretCh); + std::vector vDataCh { vopretCh }; + mtx.vout[2] = TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), mtx.vout[2].nValue, mypk, &vDataCh); // cc change + } + + mtx.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx, cpTokens, testKeys[mypk], 10000, + TokensV2::EncodeTokenOpRet(tokenid2, { unspendableAssetsPubkey }, + { AssetsV2::EncodeAssetOpRet('s', unit_price, vuint8_t(mypk.begin(), mypk.end()), expiryHeight) }))); + std::cerr << __func__ << " tokenv2ask_basic different tokenid in opdrop - should work before HF fix:" << std::endl; + EXPECT_TRUE(eval.AddTx(mtx)); // work for tokel before CCASSETS_OPDROP_FIX_TOKEL_HEIGHT + uint256 askid = mtx.GetHash(); + + // add fill ask + eval.SetCurrentHeight(CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT); // after fix activation + + CAmount fillUnits = 1; + CAmount txfee = 10000; + CMutableTransaction mtx2 = MakeTokenV2FillAskTx(cpAssets, pk2, tokenid2, askid, fillUnits, unit_price, data); + { + CScript opret = TokensV2::EncodeTokenOpRet(tokenid1, { unspendableAssetsPubkey }, {}); // put tokenid1 in opdrop while assetid = tokenid2 + vscript_t vopret; + GetOpReturnData(opret, vopret); + std::vector vData { vopret }; + mtx2.vout[0] = TokensV2::MakeTokensCC1vout(AssetsV2::EvalCode(), mtx2.vout[0].nValue, unspendableAssetsPubkey, &vData); // replace remainder with tokenid3 + } + { + CScript opret = TokensV2::EncodeTokenOpRet(tokenid1, { pk2 }, {}); + vscript_t vopret; + GetOpReturnData(opret, vopret); + std::vector vData { vopret }; + mtx2.vout[1] = TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), mtx2.vout[1].nValue, pk2, &vData); // purchased tokens + } + + mtx2.vout.pop_back(); // remove opret to replace it in TestFinalizeTx + CCwrapper wrCond1(TokensV2::MakeTokensCCcond1(TokensV2::EvalCode(), pk2)); // spend my tokens to fill buy + CCAddVintxCond(cpAssets, wrCond1, NULL); //NULL indicates to use myprivkey + + ASSERT_TRUE(TestFinalizeTx(mtx2, cpAssets, testKeys[pk2], 10000, + TokensV2::EncodeTokenOpRet(tokenid2, { pk2 }, + { AssetsV2::EncodeAssetOpRet('S', unit_price, vuint8_t(mypk.begin(), mypk.end()), expiryHeight) }))); + + //std::cerr << __func__ << " test: tokenfillask with different tokenid in opdrop, fillUnuts=" << fillUnits << " should fail with 'invalid tokenid' error:" << std::endl; + EXPECT_TRUE(!eval.TryAddTx(mtx2) && eval.state.GetRejectReason().find("invalid tokenid") != std::string::npos); // must work before TOKEL activation height + } + { + // test: try to send remainder to 2of3 token/asset eval/secp threshold (must be 3of3) + CMutableTransaction mtx1(mtx); + CScript opret = TokensV2::EncodeTokenOpRet(tokenid1, { unspendableAssetsPubkey }, {}); + vscript_t vopret; + GetOpReturnData(opret, vopret); + mtx1.vout[0] = TestMakeTokensCCMofNvoutMixed(AssetsV2::EvalCode(), TokensV2::EvalCode(), mtx1.vout[0].nValue, 1, { unspendableAssetsPubkey }, &vopret); // replace with 1of2 eval threshold + //std::cerr << __func__ << " test: tokenfillask with incorrect eval threshold:" << std::endl; + EXPECT_FALSE(eval.TryAddTx(mtx1)); // must fail: incorrect funcid + } +} + + +TEST_F(TestAssetsCC, tokenv2fillbid_basic) +{ + UniValue data(UniValue::VOBJ); + struct CCcontract_info *cpTokens, C; + cpTokens = CCinit(&C, TokensV2::EvalCode()); + + std::vector bidids = { bidid1, bidid2, bidid3 }; // bids with no extra coins and with some + int i = 0; + for (auto const & bidid : bidids) + { + eval.SetCurrentHeight(111); + + CTransaction txbid; + uint256 hashBlock; + ASSERT_TRUE(GetTxUnconfirmedOpt(&eval, bidid, txbid, hashBlock)); + + CMutableTransaction mtx = MakeTokenV2FillBidTx(cpTokens, pk2, tokenid2, bidid, 2, 0, data); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: valid tokenv2fillbid + EXPECT_TRUE(eval.TryAddTx(mtx)); + { + // test: fill with another tokenid + CMutableTransaction mtx1(mtx); + + CAmount txfee = 10000; + vuint8_t vtokenCreatorPubkey = ParseHex(data["vtokenCreatorPubkey"].getValStr()); + vuint8_t vorigpubkey = ParseHex(data["vorigpubkey"].getValStr()); + CAmount unit_price = data["unit_price"].get_int64(); + int32_t expiryHeight = data["expiryHeight"].get_int(); + + mtx1.vin[1] = CTxIn(tokenid3, 1, CScript()); // spend other tokenid3 + mtx1.vout.pop_back(); // remove opret to replace it in TestFinalizeTx + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[pk2], txfee, + TokensV2::EncodeTokenOpRet(tokenid3, { pubkey2pk(vorigpubkey) }, + { AssetsV2::EncodeAssetOpRet('B', unit_price, vorigpubkey, expiryHeight) }))); + + EXPECT_FALSE(eval.TryAddTx(mtx1)); // must fail: can't fill with another tokenid3 + } + { + // test: use opposite funcid + CMutableTransaction mtx1(mtx); + + CAmount txfee = 10000; + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txbid); // get orig pk, orig value + + mtx1.vout.pop_back(); // remove opret to replace it in TestFinalizeTx + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[pk2], txfee, + TokensV2::EncodeTokenOpRet(assetidOpret, { pubkey2pk(vorigpubkey) }, + { AssetsV2::EncodeAssetOpRet('S', unit_price, vorigpubkey, expiryHeight) }))); // 'B' -> 'S' + + EXPECT_FALSE(eval.TryAddTx(mtx1)); // must fail: incorrect funcid + } + { + // test: changed origpk in opreturn + CMutableTransaction mtx1(mtx); + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txbid); // get orig pk, orig value + mtx1.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[pk2], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { pk2 }, + { AssetsV2::EncodeAssetOpRet('B', unit_price, vuint8_t(pkunused.begin(), pkunused.end()), expiryHeight) }))); // not matched origpk (should be pk1) + EXPECT_FALSE(eval.TryAddTx(mtx1)); // must fail changed origpk + } + { + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txbid); // get orig pk, orig value + + CMutableTransaction mtx = MakeTokenV2FillBidTx(cpTokens, pk2, tokenid2, bidid, 1, unit_price, data); + mtx.vout[1].nValue += 1; // imitate lower price + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: invalid tokenv2fillbid with lower price but invalid cc inputs != cc outputs + EXPECT_FALSE(eval.TryAddTx(mtx)); // must fail + } + { + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txbid); // get orig pk, orig value + + CMutableTransaction mtx = MakeTokenV2FillBidTx(cpTokens, pk2, tokenid2, bidid, 1, unit_price-1, data); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: valid tokenv2fillbid with lower sell price than requested + EXPECT_TRUE(eval.TryAddTx(mtx)); + } + { + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txbid); // get orig pk, orig value + + CMutableTransaction mtx = MakeTokenV2FillBidTx(cpTokens, pk2, tokenid2, txbid.GetHash(), 1, unit_price+1, data); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: invalid tokenv2fillbid with bigger sell price than requested + EXPECT_FALSE(eval.TryAddTx(mtx)); // must fail + } + { + // test: fillbid with different tokenid in opdrop + //eval.SetCurrentHeight(111); + eval.SetCurrentHeight(CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT); + strcpy(ASSETCHAINS_SYMBOL, "TOKEL"); + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); + + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, txbid); // get orig pk, orig value + + CMutableTransaction mtx2 = MakeTokenV2FillBidTx(cpTokens, pk2, assetidOpret, txbid.GetHash(), 2, 0, data); + + CAmount txfee = 10000; + mtx2.vin[1] = CTxIn(tokenid3, 1, CScript()); // spend other tokenid3 + { + CScript opret = TokensV2::EncodeTokenOpRet(tokenid3, { vorigpubkey }, {}); //send to tokenid3 when assetidOpret = tokenid2 + vscript_t vopret; + GetOpReturnData(opret, vopret); + std::vector vData { vopret }; + mtx2.vout[2] = TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), mtx2.vout[2].nValue, vorigpubkey, &vData); + } + { + CScript opretCh = TokensV2::EncodeTokenOpRet(tokenid3, { pk2 }, {}); //send to tokenid3 when assetidOpret = tokenid2 + vscript_t vopretCh; + GetOpReturnData(opretCh, vopretCh); + std::vector vDataCh { vopretCh }; + mtx2.vout[3] = TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), mtx2.vout[3].nValue, pk2, &vDataCh); // change + } + + mtx2.vout.pop_back(); // remove opret to replace it in TestFinalizeTx + ASSERT_TRUE(TestFinalizeTx(mtx2, cpTokens, testKeys[pk2], txfee, + TokensV2::EncodeTokenOpRet(assetidOpret, { pubkey2pk(vorigpubkey) }, + { AssetsV2::EncodeAssetOpRet('B', unit_price, vorigpubkey, expiryHeight) }))); + + //std::cerr << __func__ << " test: tokenfillbid with different tokenid in opdrop, tokenid3=" << tokenid3.GetHex() << " assetidOpret=" << assetidOpret.GetHex() << std::endl; + //std::cerr << __func__ << " i=" << i++ << std::endl; + EXPECT_FALSE(assetidOpret == tokenid3); + //EXPECT_TRUE(eval.TryAddTx(mtx2)); + bool b = false; + EXPECT_TRUE(!(b=eval.TryAddTx(mtx2)) && eval.state.GetRejectReason().find("invalid tokenid") != std::string::npos); // must fail: can't fill with another tokenid3 in opdrop + //std::cerr <<__func__ << " b=" << b << " eval.state.GetRejectReason()=" << eval.state.GetRejectReason() << " mtx2=" << HexStr(E_MARSHAL(ss << mtx2)) << std::endl; + } + } +} + +// NOTE: no test for tokenv2fillbid_royalty_non_fixed + +TEST_F(TestAssetsCC, tokenv2fillbid_royalty_fixed) +{ + eval.SetCurrentHeight(111); //set height + + std::vector royalties = { 1, 200, 500, 800, 999 }; + //for(int r = 100; r < 1000; r += 100) + for (auto const r : royalties) + { + UniValue data(UniValue::VOBJ); // some data returned from MakeTokenV2FillBidTx + UniValue tokeldata(UniValue::VOBJ); + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + struct CCcontract_info *cpAssets, assetsC; + cpAssets = CCinit(&assetsC, AssetsV2::EvalCode()); + + tokeldata.pushKV("royalty", r); + CTransaction mytxtokencreate = MakeTokenV2CreateTx(pk1, 1, tokeldata); + uint256 mytokenid = mytxtokencreate.GetHash(); + eval.AddTx(mytxtokencreate); + + CAmount price = 4999; + CAmount asktokens = 1; + CAmount filltokens = 1; + + //CTransaction mytxbid = MakeTokenV2BidTx(cpAssets, pk2, mytokenid, 1, ASSETS_NORMAL_DUST*2+1, 222); + CTransaction mytxbid = MakeTokenV2BidTx(cpAssets, pk2, mytokenid, asktokens, price, 222); + eval.AddTx(mytxbid); + + CMutableTransaction mytxfillbid = MakeTokenV2FillBidTx(cpTokens, pk1, mytokenid, mytxbid.GetHash(), filltokens, 0, data); + ASSERT_FALSE(CTransaction(mytxfillbid).IsNull()); + ASSERT_TRUE(data["royaltyFract"].get_int64() == r); + if (filltokens * price / (int64_t)TKLROYALTY_DIVISOR * std::min(r, (int32_t)TKLROYALTY_DIVISOR - r) <= ASSETS_NORMAL_DUST) { + // have dust + EXPECT_TRUE(data["royaltyValue"].get_int64() == 0); + EXPECT_TRUE(eval.TryAddTx(mytxfillbid)); + EXPECT_TRUE(mytxfillbid.vout[1].nValue == filltokens * price); + EXPECT_TRUE(r < TKLROYALTY_DIVISOR/2 ? data["isRoyaltyDust"].get_bool() : !data["isRoyaltyDust"].get_bool()); // is dust royalty or paid_value + // if royalty is not dust then the value should go either to tokenowner + if (r >= TKLROYALTY_DIVISOR/2) + EXPECT_TRUE(IsEqualDestinations(mytxfillbid.vout[1].scriptPubKey, CScript() << vuint8_t(pk1.begin(), pk1.end()) << OP_CHECKSIG)); + } + else { + // no dust + EXPECT_TRUE(data["royaltyValue"].get_int64() > 0); + EXPECT_TRUE(eval.TryAddTx(mytxfillbid)); + EXPECT_TRUE(mytxfillbid.vout[1].nValue + mytxfillbid.vout[2].nValue == filltokens * price); + } + + // test: valid tokenv2fillbid + EXPECT_TRUE(eval.TryAddTx(mytxfillbid)); + } +} + +// test old incorrect validation rule that failed royalties > 50% +TEST_F(TestAssetsCC, tokenv2fillask_royalty_non_fixed) +{ + eval.SetCurrentTime(CCUpgrades::CCMIXEDMODE_SUBVER_1_TOKEL_TIMESTAMP - 1); + strcpy(ASSETCHAINS_SYMBOL, "TOKEL"); + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); + + ASSERT_FALSE(CCUpgrades::IsUpgradeActive(eval.GetCurrentTime(), eval.GetCurrentHeight(), CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)); + for(int r = 100; r < 1000; r += 100) + { + UniValue data(UniValue::VOBJ); // some data returned from MakeTokenV2FillBidTx + UniValue tokeldata(UniValue::VOBJ); + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + struct CCcontract_info *cpAssets, assetsC; + cpAssets = CCinit(&assetsC, AssetsV2::EvalCode()); + + tokeldata.pushKV("royalty", r); + CTransaction mytxtokencreate = MakeTokenV2CreateTx(pk1, 1, tokeldata); + uint256 mytokenid = mytxtokencreate.GetHash(); + eval.AddTx(mytxtokencreate); + + CAmount price = 211110000; + CAmount asktokens = 1; + CAmount filltokens = 1; + + CTransaction mytxask = MakeTokenV2AskTx(cpTokens, pk1, mytokenid, asktokens, price, eval.GetCurrentHeight() + 222); + eval.AddTx(mytxask); + + CMutableTransaction mytxfillask = MakeTokenV2FillAskTx(cpAssets, pk2, mytokenid, mytxask.GetHash(), filltokens, 0, data); + ASSERT_FALSE(CTransaction(mytxfillask).IsNull()); + ASSERT_TRUE(data["royaltyFract"].get_int64() == r); + ASSERT_TRUE(data["royaltyValue"].get_int64() > 0); // must not be dust for this test + + if (r <= 500) + EXPECT_TRUE(eval.TryAddTx(mytxfillask)); // valid fillask if royalty <= 50% + else + EXPECT_FALSE(eval.TryAddTx(mytxfillask)); + } +} + +// test fill ask updated validation rule +TEST_F(TestAssetsCC, tokenv2fillask_royalty_fixed) +{ + eval.SetCurrentTime(CCUpgrades::CCMIXEDMODE_SUBVER_1_TOKEL_TIMESTAMP); + strcpy(ASSETCHAINS_SYMBOL, "TOKEL"); + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); + + ASSERT_TRUE(CCUpgrades::IsUpgradeActive(eval.GetCurrentTime(), eval.GetCurrentHeight(), CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)); // fix in action + for(int r = 100; r < 1000; r += 100) + { + UniValue data(UniValue::VOBJ); // some data returned from MakeTokenV2FilAskTx + UniValue tokeldata(UniValue::VOBJ); + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + struct CCcontract_info *cpAssets, assetsC; + cpAssets = CCinit(&assetsC, AssetsV2::EvalCode()); + + tokeldata.pushKV("royalty", r); + CTransaction mytxtokencreate = MakeTokenV2CreateTx(pk1, 1, tokeldata); + uint256 mytokenid = mytxtokencreate.GetHash(); + eval.AddTx(mytxtokencreate); + CAmount price = 4999; + CAmount asktokens = 1; + CAmount filltokens = 1; + // CTransaction mytxask = MakeTokenV2AskTx(cpTokens, pk1, mytokenid, 1, ASSETS_NORMAL_DUST*2+1, 222); + CTransaction mytxask = MakeTokenV2AskTx(cpTokens, pk1, mytokenid, asktokens, price, eval.GetCurrentHeight() + 222); + eval.AddTx(mytxask); + + CMutableTransaction mytxfillask = MakeTokenV2FillAskTx(cpAssets, pk2, mytokenid, mytxask.GetHash(), filltokens, 0, data); + ASSERT_FALSE(CTransaction(mytxfillask).IsNull()); + ASSERT_TRUE(data["royaltyFract"].get_int64() == r); + + //if (r == 100 || r == 900) EXPECT_TRUE(data["royaltyValue"].get_int64() == 0); // for these royalties t should be dust + + // test: valid tokenv2fillask + //std::cerr << __func__ << " good rule, is dust calc=" << (filltokens * price / (int64_t)TKLROYALTY_DIVISOR * std::min(r, (int32_t)TKLROYALTY_DIVISOR - r)) << " royaltyValue=" << data["royaltyValue"].get_int64() << std::endl; + if (filltokens * price / (int64_t)TKLROYALTY_DIVISOR * std::min(r, (int32_t)TKLROYALTY_DIVISOR - r) <= ASSETS_NORMAL_DUST) { + // have dust + EXPECT_TRUE(data["royaltyValue"].get_int64() == 0); + EXPECT_TRUE(eval.TryAddTx(mytxfillask)); + EXPECT_TRUE(mytxfillask.vout[2].nValue == filltokens * price); + EXPECT_TRUE(r < TKLROYALTY_DIVISOR/2 ? data["isRoyaltyDust"].get_bool() : !data["isRoyaltyDust"].get_bool()); // is dust royalty or paid_value + } + else { + // no dust + EXPECT_TRUE(data["royaltyValue"].get_int64() > 0); + EXPECT_TRUE(eval.TryAddTx(mytxfillask)); + EXPECT_TRUE(mytxfillask.vout[2].nValue + mytxfillask.vout[3].nValue == filltokens * price); + } + } +} + + +TEST_F(TestAssetsCC, tokenv2cancelask) +{ + struct CCcontract_info *cpAssets, C; + struct CCcontract_info *cpTokens, tokensC; + eval.SetCurrentHeight(111); //set height + + CAmount txfee = 10000; + + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + CTransaction txask1; + uint256 hashBlock; + ASSERT_TRUE(GetTxUnconfirmedOpt(&eval, askid1, txask1, hashBlock)); + + for (CTransaction vintx : std::vector{ txask1 }) // TODO add more txasks + { + UniValue data(UniValue::VOBJ); + uint256 asktxid = vintx.GetHash(); + + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, vintx); + + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + CMutableTransaction mtx = MakeTokenV2CancelAskTx(cpAssets, pubkey2pk(vorigpubkey), tokenid1, asktxid, data); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + CAmount askamount = data["askamount"].get_int64(); + + // test: valid tokenv2cancelask + EXPECT_TRUE(eval.TryAddTx(mtx)); + { + // test: invalid pk2 in assets opreturn + CMutableTransaction mtx2(mtx); + mtx2.vout.pop_back(); + mtx2.vout.push_back(CTxOut(0, TokensV2::EncodeTokenOpRet(tokenid1, { vorigpubkey }, + { AssetsV2::EncodeAssetOpRet('x', 0, vuint8_t(pk2.begin(), pk2.end()), 0) } ))); + EXPECT_TRUE(eval.TryAddTx(mtx2)); // pk in opret not checked + } + { + // test: some unused pk in token opret + CMutableTransaction mtx3(mtx); + mtx3.vout.pop_back(); + mtx3.vout.push_back(CTxOut(0, TokensV2::EncodeTokenOpRet(tokenid1, { pkunused }, + { AssetsV2::EncodeAssetOpRet('x', 0, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), 0) } ))); + EXPECT_TRUE(eval.TryAddTx(mtx3)); // pk in opret is not checked for token v2, it is only for a hint + } + { + // test: invalid pk where to funds sent + CMutableTransaction mtx4(mtx); + mtx4.vout[0] = TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), askamount, pk2); + EXPECT_FALSE(eval.TryAddTx(mtx4)); // must fail + } + { + // test: invalid tokenid in token opret + CMutableTransaction mtx5(mtx); + mtx5.vout.pop_back(); + mtx5.vout.push_back(CTxOut(0, TokensV2::EncodeTokenOpRet(tokenidUnused, { vorigpubkey }, + { AssetsV2::EncodeAssetOpRet('x', 0, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), 0) } ))); + EXPECT_FALSE(eval.TryAddTx(mtx5)); // must fail: cant send to another tokenid + } + { + // test: invalid funcid in token opret + CMutableTransaction mtx6(mtx); + mtx6.vout.pop_back(); + mtx6.vout.push_back(CTxOut(0, TokensV2::EncodeTokenOpRet(tokenid1, { vorigpubkey }, + { AssetsV2::EncodeAssetOpRet('o', 0, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), 0) } ))); + EXPECT_FALSE(eval.TryAddTx(mtx6)); // must fail + } + { + eval.SetCurrentHeight(CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT); + strcpy(ASSETCHAINS_SYMBOL, "TOKEL"); + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); + + CMutableTransaction mtx7(mtx); + + CAmount txfee = 10000; + CAmount otherAmount = TestAddTokenInputs(mtx7, vorigpubkey, tokenid4, mtx7.vout[0].nValue); //add different tokenid + ASSERT_TRUE(otherAmount > 0); + + mtx7.vout.insert(mtx7.vout.begin() + mtx7.vout.size() - 1, mtx7.vout[0]); // copy vout with tokens with assetidOpret back + + CScript opret = TokensV2::EncodeTokenOpRet(tokenid4, { vorigpubkey }, {}); // put tokenid4 in opdrop while assetid differs + vscript_t vopret; + GetOpReturnData(opret, vopret); + std::vector vData { vopret }; + mtx7.vout[0] = TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), mtx7.vout[0].nValue, vorigpubkey, &vData); // replace remainder with tokenid4 + + //std::cerr << __func__ << " tokenid4=" << tokenid4.GetHex() << " assetidOpret=" << assetidOpret.GetHex() << " otherAmount=" << otherAmount << " mtx7.vin.size()=" << mtx7.vin.size() << std::endl; + if (otherAmount > mtx7.vout[0].nValue) { // if tokenid4 change exists + CScript opret = TokensV2::EncodeTokenOpRet(tokenid4, { vorigpubkey }, {}); + vscript_t vopret; + GetOpReturnData(opret, vopret); + std::vector vData { vopret }; + mtx7.vout.insert(mtx7.vout.begin() + mtx7.vout.size() - 1, + TokensV2::MakeTokensCC1vout(TokensV2::EvalCode(), otherAmount - mtx7.vout[0].nValue, vorigpubkey, &vData)); // add tokenid4 change + } + + CCwrapper wrCond1(TokensV2::MakeTokensCCcond1(TokensV2::EvalCode(), vorigpubkey)); // spend my tokenid4 + CCAddVintxCond(cpAssets, wrCond1, NULL); // NULL indicates to use myprivkey + + mtx7.vout.pop_back(); //remove opret + ASSERT_TRUE(TestFinalizeTx(mtx7, cpAssets, testKeys[vorigpubkey], 10000, + TokensV2::EncodeTokenOpRet(assetidOpret, { vorigpubkey }, + { AssetsV2::EncodeAssetOpRet('x', 0, vuint8_t(), 0) }))); + + //std::cerr << __func__ << " test: tokencancelask with different tokenid in opdrop" << std::endl; + EXPECT_FALSE(assetidOpret == tokenid4); + EXPECT_TRUE(!eval.TryAddTx(mtx7) && eval.state.GetRejectReason().find("invalid tokenid") != std::string::npos); // must fail: can't cancel with another tokenid in opdrop + } + } +} + +TEST_F(TestAssetsCC, tokenv2cancelask_expired) +{ + CAmount txfee = 10000; + eval.SetCurrentHeight(111); //set height + { + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + + CTransaction txaskexp = MakeTokenV2AskTx(cpTokens, pk1, tokenid1, 2, 10000, 222); // set expiry height 222 + ASSERT_FALSE(CTransaction(txaskexp).IsNull()); + EXPECT_TRUE(eval.AddTx(txaskexp)); + + UniValue data(UniValue::VOBJ); + uint256 asktxid = txaskexp.GetHash(); + + eval.SetCurrentHeight(111); + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + + CMutableTransaction mtx = MakeTokenV2CancelAskTx(cpAssets, pk1, tokenid1, asktxid, data); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: valid tokenv2cancelask with mypk + EXPECT_TRUE(eval.TryAddTx(mtx)); + } + { + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + + CTransaction txaskexp = MakeTokenV2AskTx(cpTokens, pk1, tokenid1, 2, 10000, 222); // set expiry height 222 + ASSERT_FALSE(CTransaction(txaskexp).IsNull()); + EXPECT_TRUE(eval.AddTx(txaskexp)); + + UniValue data(UniValue::VOBJ); + uint256 asktxid = txaskexp.GetHash(); + + eval.SetCurrentHeight(111); //set height not expired + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + + CMutableTransaction mtx = MakeTokenV2CancelAskTx(cpAssets, pk2, tokenid1, asktxid, data); // use another pk (global pk will be used actually) + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: try tokenv2cancelask not yet expired with globalpk + EXPECT_FALSE(eval.TryAddTx(mtx)); // should fail + } + { + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + + // use static mtx as it is added to static eval + CTransaction txaskexp = MakeTokenV2AskTx(cpTokens, pk1, tokenid1, 2, 10000, 222); // set expiry height 222 + ASSERT_FALSE(CTransaction(txaskexp).IsNull()); + EXPECT_TRUE(eval.AddTx(txaskexp)); + + UniValue data(UniValue::VOBJ); + uint256 asktxid = txaskexp.GetHash(); + + eval.SetCurrentHeight(223); + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + + CMutableTransaction mtx = MakeTokenV2CancelAskTx(cpAssets, pk2, tokenid1, asktxid, data); // use another pk (global pk will be used actually) + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: try tokenv2cancelask already expired with globalpk + EXPECT_TRUE(eval.TryAddTx(mtx)); // should not fail + } +} + +TEST_F(TestAssetsCC, tokenv2fillask_expired) +{ + CAmount txfee = 10000; + eval.SetCurrentHeight(111); //set height + { + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + + CTransaction txaskexp = MakeTokenV2AskTx(cpTokens, pk1, tokenid1, 2, 10000, 222); // set expiry height 222 + ASSERT_FALSE(CTransaction(txaskexp).IsNull()); + EXPECT_TRUE(eval.AddTx(txaskexp)); + + UniValue data(UniValue::VOBJ); + uint256 asktxid = txaskexp.GetHash(); + + eval.SetCurrentHeight(222); //set height expired + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + + CMutableTransaction mtx = MakeTokenV2FillAskTx(cpAssets, pk2, tokenid1, asktxid, 1, 0, data); + + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: try tokenv2fillask already expired + EXPECT_FALSE(eval.TryAddTx(mtx)); // should fail + } +} + +TEST_F(TestAssetsCC, tokenv2cancelbid) +{ + struct CCcontract_info *cpAssets, C; + struct CCcontract_info *cpTokens, tokensC; + CAmount txfee = 10000; + eval.SetCurrentHeight(111); //set height + + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + + CTransaction txbid1; + uint256 hashBlock; + ASSERT_TRUE(GetTxUnconfirmedOpt(&eval, bidid1, txbid1, hashBlock)); + + for (CTransaction vintx : std::vector{ txbid1 }) // TODO add more txbid + { + UniValue data(UniValue::VOBJ); + uint256 bidtxid = vintx.GetHash(); + + vuint8_t vorigpubkey; + CAmount unit_price; + uint256 assetidOpret; + int32_t expiryHeight; + uint8_t funcid = GetOrderParams(vorigpubkey, unit_price, assetidOpret, expiryHeight, vintx); + + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + CMutableTransaction mtx = MakeTokenV2CancelBidTx(cpAssets, pubkey2pk(vorigpubkey), tokenid2, bidtxid, data); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + CAmount bidamount = data["bidamount"].get_int64(); + + // test: valid tokenv2cancelbid + EXPECT_TRUE(eval.TryAddTx(mtx)); + { + // test: invalid pk in assets opreturn + CMutableTransaction mtx2(mtx); + mtx2.vout.pop_back(); + mtx2.vout.push_back(CTxOut(0, TokensV2::EncodeTokenOpRet(tokenid2, { vorigpubkey }, + { AssetsV2::EncodeAssetOpRet('o', 0, vuint8_t(pk1.begin(), pk1.end()), 0) } ))); + EXPECT_TRUE(eval.TryAddTx(mtx2)); // pk in opret is not checked + } + { + // test: invalid pk in token opret + CMutableTransaction mtx3(mtx); + mtx3.vout.pop_back(); + mtx3.vout.push_back(CTxOut(0, TokensV2::EncodeTokenOpRet(tokenid2, { pkunused }, + { AssetsV2::EncodeAssetOpRet('o', 0, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), 0) } ))); + EXPECT_TRUE(eval.TryAddTx(mtx3)); // pk in opret is not checked + } + { + // test: invalid pk where to funds sent + CMutableTransaction mtx4(mtx); + mtx4.vout[0] = CTxOut(bidamount, CScript() << ParseHex(HexStr(pkunused)) << OP_CHECKSIG); + EXPECT_FALSE(eval.TryAddTx(mtx4)); // must fail as can't send the remainder to another pk + } + { + // test: invalid tokenid in token opret + CMutableTransaction mtx5(mtx); + mtx5.vout.pop_back(); + mtx5.vout.push_back(CTxOut(0, TokensV2::EncodeTokenOpRet(tokenidUnused, { vorigpubkey }, + { AssetsV2::EncodeAssetOpRet('o', 0, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), 0) } ))); + EXPECT_FALSE(eval.TryAddTx(mtx5)); + } + { + // test: invalid funcid in token opret + CMutableTransaction mtx6(mtx); + mtx6.vout.pop_back(); + mtx6.vout.push_back(CTxOut(0, TokensV2::EncodeTokenOpRet(tokenid2, { pk2 }, + { AssetsV2::EncodeAssetOpRet('x', 0, vuint8_t(vorigpubkey.begin(), vorigpubkey.end()), 0) } ))); + EXPECT_FALSE(eval.TryAddTx(mtx6)); + } + { + // test: send dust to normals - bad test params here + //CMutableTransaction mtx7(mtx); + //mtx7.vout[0] = CTxOut(bidamount, CScript() << ParseHex(HexStr(vtokenCreatorPubkey)) << OP_CHECKSIG); + //EXPECT_FALSE(eval.TryAddTx(mtx7)); // must fail: dust should stay on cc global output + } + } +} + +TEST_F(TestAssetsCC, tokenv2cancelbid_expired) +{ + CAmount txfee = 10000; + eval.SetCurrentHeight(111); //set height + + { + struct CCcontract_info *cpAssets0, C0; + cpAssets0 = CCinit(&C0, AssetsV2::EvalCode()); + + // use static mtx as it is added to static eval + CTransaction txbidexp = MakeTokenV2BidTx(cpAssets0, pk1, tokenid1, 2, 10000, 222); // set expiry height 222 + ASSERT_FALSE(CTransaction(txbidexp).IsNull()); + EXPECT_TRUE(eval.AddTx(txbidexp)); + + + UniValue data(UniValue::VOBJ); + uint256 bidtxid = txbidexp.GetHash(); + + eval.SetCurrentHeight(111); + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + + CMutableTransaction mtx = MakeTokenV2CancelBidTx(cpAssets, pk1, tokenid1, bidtxid, data); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: valid tokenv2cancelbid with mypk + EXPECT_TRUE(eval.TryAddTx(mtx)); + } + { + struct CCcontract_info *cpAssets0, C0; + cpAssets0 = CCinit(&C0, AssetsV2::EvalCode()); + + CTransaction txbidexp = MakeTokenV2BidTx(cpAssets0, pk1, tokenid1, 2, 10000, 222); // set expiry height 222 + ASSERT_FALSE(CTransaction(txbidexp).IsNull()); + EXPECT_TRUE(eval.AddTx(txbidexp)); + + UniValue data(UniValue::VOBJ); + uint256 bidtxid = txbidexp.GetHash(); + + eval.SetCurrentHeight(111); //set height not expired + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + + CMutableTransaction mtx = MakeTokenV2CancelBidTx(cpAssets, pk2, tokenid1, bidtxid, data); // use another pk (global pk will be used actually) + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: try tokenv2cancelbid not yet expired with globalpk + EXPECT_FALSE(eval.TryAddTx(mtx)); // should fail + } + { + struct CCcontract_info *cpAssets0, C0; + cpAssets0 = CCinit(&C0, AssetsV2::EvalCode()); + + // use static mtx as it is added to static eval + CTransaction txbidexp = MakeTokenV2BidTx(cpAssets0, pk1, tokenid1, 2, 10000, 222); // set expiry height 222 + ASSERT_FALSE(CTransaction(txbidexp).IsNull()); + EXPECT_TRUE(eval.AddTx(txbidexp)); + + UniValue data(UniValue::VOBJ); + uint256 bidtxid = txbidexp.GetHash(); + + eval.SetCurrentHeight(223); + struct CCcontract_info *cpAssets, C; + cpAssets = CCinit(&C, AssetsV2::EvalCode()); + + CMutableTransaction mtx = MakeTokenV2CancelBidTx(cpAssets, pk2, tokenid1, bidtxid, data); // use another pk (global pk will be used actually) + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: try tokenv2cancelbid already expired with globalpk + EXPECT_TRUE(eval.TryAddTx(mtx)); // should not fail + } +} + +TEST_F(TestAssetsCC, tokenv2fillbid_expired) +{ + CAmount txfee = 10000; + eval.SetCurrentHeight(111); //set height + { + struct CCcontract_info *cpAssets, assetsC; + cpAssets = CCinit(&assetsC, AssetsV2::EvalCode()); + + CTransaction txbidexp = MakeTokenV2BidTx(cpAssets, pk1, tokenid2, 2, 10000, 222); // set expiry height 222 + ASSERT_FALSE(CTransaction(txbidexp).IsNull()); + EXPECT_TRUE(eval.AddTx(txbidexp)); + + UniValue data(UniValue::VOBJ); + uint256 bidtxid = txbidexp.GetHash(); + + eval.SetCurrentHeight(222); //set height expired + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, TokensV2::EvalCode()); + + CMutableTransaction mtx = MakeTokenV2FillBidTx(cpTokens, pk2, tokenid2, bidtxid, 1, 0, data); + + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + // test: try tokenv2fillask already expired + EXPECT_FALSE(eval.TryAddTx(mtx)); // should fail + } +} + + +/* ---------------------------------------------------------------------------------------------------------------------------------- */ +// tokens tests: + +TEST_F(TestAssetsCC, tokenv2create) +{ + struct CCcontract_info *cpTokens, C; + cpTokens = CCinit(&C, TokensV2::EvalCode()); + + eval.SetCurrentHeight(111); //set height + + CMutableTransaction mtx = MakeTokenV2CreateTx(pk1, 10); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + EXPECT_TRUE(eval.TryAddTx(mtx)); + + CAmount txfee = 0; + std::string name = "T2"; + std::string description = "desc2"; + { + // test: token sent to another pk + CMutableTransaction mtx1(mtx); + mtx1.vout[1] = TokensV2::MakeTokensCC1vout(0, 10, pk2); + mtx1.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[pk1], txfee, + TokensV2::EncodeTokenCreateOpRet(vscript_t(pk1.begin(), pk1.end()), name, description, { }))); + EXPECT_TRUE(eval.TryAddTx(mtx1)); // allow sending created tokens to any pk!! + } + { + // test: invalid pk in opreturn: + CMutableTransaction mtx2(mtx); + mtx2.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx2, cpTokens, testKeys[pk1], txfee, + TokensV2::EncodeTokenCreateOpRet(vscript_t(pk2.begin(), pk2.end()), name, description, { }))); + EXPECT_FALSE(eval.TryAddTx(mtx2)); // must fail + } + { + // test: no token vouts, sent to normal + CMutableTransaction mtx3(mtx); + mtx3.vout[1] = CTxOut(10, CScript() << ParseHex(HexStr(pk1)) << OP_CHECKSIG); + mtx3.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx3, cpTokens, testKeys[pk1], txfee, + TokensV2::EncodeTokenCreateOpRet(vscript_t(pk1.begin(), pk1.end()), name, description, { }))); + EXPECT_FALSE(eval.TryAddTx(mtx3)); // fail for no token cc vouts + } + { + // test: no token vouts, sent to cc v1 + CMutableTransaction mtx4(mtx); + mtx4.vout[1] = TokensV1::MakeCC1vout(EVAL_ASSETS, 10, pk2); // sent to cc v1 vout + mtx4.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx4, cpTokens, testKeys[pk1], txfee, + TokensV2::EncodeTokenCreateOpRet(vscript_t(pk1.begin(), pk1.end()), name, description, { }))); + EXPECT_FALSE(eval.TryAddTx(mtx4)); // fail for no token cc vouts + } + { + // test: invalid token created with global pk: + CMutableTransaction mtx5 = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + uint8_t privkeyg[32]; + CPubKey pkg = GetUnspendable(cpTokens, privkeyg); + + ASSERT_TRUE(TestAddNormalInputs(mtx5, pkg, TOKENS_MARKER_VALUE + 10 + txfee) > 0); + //mtx5.vin.push_back(CTxIn(txnormalg.GetHash(), 0)); + mtx5.vout.push_back(TokensV2::MakeCC1vout(TokensV2::EvalCode(), TOKENS_MARKER_VALUE, GetUnspendable(cpTokens, NULL))); + mtx5.vout.push_back(TokensV2::MakeTokensCC1vout(0, 10, pk1)); + + ASSERT_TRUE(TestFinalizeTx(mtx5, cpTokens, privkeyg, txfee, + TokensV2::EncodeTokenCreateOpRet(vscript_t(pkg.begin(), pkg.end()), name, description, { }))); + EXPECT_FALSE(eval.TryAddTx(mtx5)); // must fail + } +} + + +TEST_F(TestAssetsCC, tokenv2create_bad) +{ + struct CCcontract_info *cpTokens, C; + cpTokens = CCinit(&C, TokensV2::EvalCode()); + + eval.SetCurrentHeight(111); //set height + + CMutableTransaction mtx = MakeTokenV2CreateTx(pk1, 10); + ASSERT_FALSE(CTransaction(mtx).IsNull()); + + CAmount txfee = 10000; + std::string name = "T2"; + std::string description = "desc2"; + { + // test: token sent to another pk + CMutableTransaction mtx1(mtx); + mtx1.vout[1] = TokensV2::MakeTokensCC1vout(0, 10, pk2); + mtx1.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx1, cpTokens, testKeys[pk1], txfee, + TokensV2::EncodeTokenCreateOpRet(vscript_t(pk1.begin(), pk1.end()), name, description, { }))); + EXPECT_TRUE(eval.TryAddTx(mtx1)); // allow sending created tokens to any pk!! + } + { + // test: invalid pk in opreturn: + CMutableTransaction mtx2(mtx); + mtx2.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx2, cpTokens, testKeys[pk1], txfee, + TokensV2::EncodeTokenCreateOpRet(vscript_t(pk2.begin(), pk2.end()), name, description, { }))); + EXPECT_FALSE(eval.TryAddTx(mtx2)); // must fail + } + { + // test: no token vouts, sent to normal + CMutableTransaction mtx3(mtx); + mtx3.vout[1] = CTxOut(10, CScript() << ParseHex(HexStr(pk1)) << OP_CHECKSIG); + mtx3.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx3, cpTokens, testKeys[pk1], txfee, + TokensV2::EncodeTokenCreateOpRet(vscript_t(pk1.begin(), pk1.end()), name, description, { }))); + EXPECT_FALSE(eval.TryAddTx(mtx3)); // fail for no token cc vouts + } + { + // test: no token vouts, sent to cc v1 + CMutableTransaction mtx4(mtx); + mtx4.vout[1] = TokensV1::MakeCC1vout(EVAL_ASSETS, 10, pk2); // sent to cc v1 vout + mtx4.vout.pop_back(); + ASSERT_TRUE(TestFinalizeTx(mtx4, cpTokens, testKeys[pk1], txfee, + TokensV2::EncodeTokenCreateOpRet(vscript_t(pk1.begin(), pk1.end()), name, description, { }))); + EXPECT_FALSE(eval.TryAddTx(mtx4)); // fail for no token cc vouts + } + { + // test: invalid token created with global pk: + CMutableTransaction mtx5 = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + uint8_t privkeyg[32]; + CPubKey pkg = GetUnspendable(cpTokens, privkeyg); + + ASSERT_TRUE(TestAddNormalInputs(mtx5, pkg, TOKENS_MARKER_VALUE + 10 + txfee) > 0); + //mtx5.vin.push_back(CTxIn(txnormalg.GetHash(), 0)); + mtx5.vout.push_back(TokensV2::MakeCC1vout(TokensV2::EvalCode(), TOKENS_MARKER_VALUE, GetUnspendable(cpTokens, NULL))); + mtx5.vout.push_back(TokensV2::MakeTokensCC1vout(0, 10, pk1)); + + ASSERT_TRUE(TestFinalizeTx(mtx5, cpTokens, privkeyg, txfee, + TokensV2::EncodeTokenCreateOpRet(vscript_t(pkg.begin(), pkg.end()), name, description, { }))); + EXPECT_FALSE(eval.TryAddTx(mtx5)); // must fail + } +} + +TEST_F(TestAssetsCC, tokenv2transfer) +{ + struct CCcontract_info *cpTokens, C; + cpTokens = CCinit(&C, TokensV2::EvalCode()); + CAmount crAmount = 10, trAmount = 1; + uint8_t M_1 = 1; + + + eval.SetCurrentHeight(111); //set height + + CMutableTransaction mtxCreate = MakeTokenV2CreateTx(pk1, crAmount); + ASSERT_FALSE(CTransaction(mtxCreate).IsNull()); + EXPECT_TRUE(eval.AddTx(mtxCreate)); + uint256 mytokenid = mtxCreate.GetHash(); + + CScript opret; + CMutableTransaction mtxTransfer = BeginTokenV2TransferTx(pk1); + ASSERT_FALSE(CTransaction(mtxTransfer).IsNull()); + ASSERT_TRUE(AddTokenV2TransferOutputs(cpTokens, mtxTransfer, pk1, mytokenid, "", {}, M_1, {pk2}, trAmount, true, opret)); + ASSERT_TRUE(FinalizeTokenV2TransferTx(cpTokens, mtxTransfer, pk1, opret)); + EXPECT_TRUE(eval.AddTx(mtxTransfer)); + + EXPECT_TRUE(TokenV2Balance(pk1, mytokenid) == crAmount - trAmount); + EXPECT_TRUE(TokenV2Balance(pk2, mytokenid) == trAmount); +} + +TEST_F(TestAssetsCC, tokenv2transfermany) +{ + uint8_t M_1 = 1; + eval.SetCurrentHeight(111); //set height + + CAmount values[][3] = { { 1, 1, 1 }, { 100, 2, 2 } }; + + for (int i = 0; i < sizeof(values)/sizeof(values[0]); i ++) + { + CAmount crAmount = values[i][0]; + CAmount trAmount = values[i][1]; + int32_t splitOutputs = values[i][2]; + for (const auto skipInputs : { false, true }) + { + for(const auto corruptChange : { 0LL, 1LL, -1LL }) + { + for (const auto useOpReturn : { false, true }) // try 1) both vouts opdrop and 2) one opdrop, second opreturn + { + CMutableTransaction mtxCreate1 = MakeTokenV2CreateTx(pk1, crAmount); + ASSERT_FALSE(CTransaction(mtxCreate1).IsNull()); + EXPECT_TRUE(eval.AddTx(mtxCreate1)); + + CMutableTransaction mtxCreate2 = MakeTokenV2CreateTx(pk1, crAmount); + ASSERT_FALSE(CTransaction(mtxCreate2).IsNull()); + EXPECT_TRUE(eval.AddTx(mtxCreate2)); + + uint256 mytokenid1 = mtxCreate1.GetHash(); + uint256 mytokenid2 = mtxCreate2.GetHash(); + + CScript opret1; + struct CCcontract_info *cpTokens1, C1; + cpTokens1 = CCinit(&C1, TokensV2::EvalCode()); + CMutableTransaction mtxTransfer1 = BeginTokenV2TransferTx(pk1); + ASSERT_FALSE(CTransaction(mtxTransfer1).IsNull()); + ASSERT_TRUE(AddTokenV2TransferOutputs(cpTokens1, mtxTransfer1, pk1, mytokenid1, "", {}, M_1, {pk2}, trAmount, false, opret1)); + ASSERT_TRUE(AddTokenV2TransferOutputs(cpTokens1, mtxTransfer1, pk1, mytokenid2, "", {}, M_1, {pk2}, trAmount, useOpReturn, opret1, false, 0LL, splitOutputs)); + ASSERT_TRUE(FinalizeTokenV2TransferTx(cpTokens1, mtxTransfer1, pk1, opret1)); + EXPECT_TRUE(eval.AddTx(mtxTransfer1)); + + EXPECT_TRUE(TokenV2Balance(pk1, mytokenid1) == crAmount - trAmount); + EXPECT_TRUE(TokenV2Balance(pk2, mytokenid1) == trAmount); + EXPECT_TRUE(TokenV2Balance(pk1, mytokenid2) == crAmount - trAmount); + EXPECT_TRUE(TokenV2Balance(pk2, mytokenid2) == trAmount); + + // transfer back: + CScript opret2; + struct CCcontract_info *cpTokens2, C2; + cpTokens2 = CCinit(&C2, TokensV2::EvalCode()); + CMutableTransaction mtxTransfer2 = BeginTokenV2TransferTx(pk2); + ASSERT_FALSE(CTransaction(mtxTransfer2).IsNull()); + ASSERT_TRUE(AddTokenV2TransferOutputs(cpTokens2, mtxTransfer2, pk2, mytokenid1, "", {}, M_1, {pk1}, trAmount, false, opret2)); + ASSERT_TRUE(AddTokenV2TransferOutputs(cpTokens2, mtxTransfer2, pk2, mytokenid2, "", {}, M_1, {pk1}, trAmount, useOpReturn, opret2, skipInputs, corruptChange)); + ASSERT_TRUE(FinalizeTokenV2TransferTx(cpTokens2, mtxTransfer2, pk2, opret2)); + + if (skipInputs || corruptChange != 0LL) { + EXPECT_FALSE(eval.AddTx(mtxTransfer2)); + continue; // invalid tx, continue... + } + else + EXPECT_TRUE(eval.AddTx(mtxTransfer2)); + + EXPECT_TRUE(TokenV2Balance(pk1, mytokenid1) == crAmount); + EXPECT_TRUE(TokenV2Balance(pk2, mytokenid1) == 0); + EXPECT_TRUE(TokenV2Balance(pk1, mytokenid2) == crAmount); + EXPECT_TRUE(TokenV2Balance(pk2, mytokenid2) == 0); + } + } + } + } +} + +// test transfer to R-address +TEST_F(TestAssetsCC, tokenv2transfer_raddress) +{ + struct CCcontract_info *cpTokens, C; + cpTokens = CCinit(&C, TokensV2::EvalCode()); + + eval.SetCurrentHeight(111); + + UniValue tokeldata(UniValue::VOBJ); + tokeldata.pushKV("id", 1); // add nft extra data as burning tokens works only if there is extra data in tokencreate + CMutableTransaction mcreatetx = MakeTokenV2CreateTx(pk1, 1, tokeldata); + ASSERT_FALSE(CTransaction(mcreatetx).IsNull()); + EXPECT_TRUE(eval.AddTx(mcreatetx)); + uint256 tokenid = mcreatetx.GetHash(); + + std::vector dests = { pk2.GetID() }; // dest R-address + + std::vector tokenaddrs1 = GetTokenV2IndexKeys(pk1); + CMutableTransaction mtransfertx1 = MakeTokenV2TransferTx(pk1, 0, tokenid, tokenaddrs1, {}, 1, dests, 1, false, false); + ASSERT_FALSE(CTransaction(mtransfertx1).IsNull()); + EXPECT_TRUE(eval.AddTx(mtransfertx1)); + CAmount bal1 = TokenV2Balance(pk2, tokenid); + // std::cerr << __func__ << " TokenV2Balance(pk2, tokenid)=" << bal << " mtransfertxid=" << mtransfertx.GetHash().GetHex() << " reason=" << eval.state.GetRejectReason() << std::endl; + EXPECT_TRUE(bal1 == 1); + + // try to spend + std::vector tokenaddrs2 = GetTokenV2IndexKeys(pk2); + CMutableTransaction mtransfertx2 = MakeTokenV2TransferTx(pk2, 0, tokenid, tokenaddrs2, {}, 1, dests, 1, false, false); + ASSERT_FALSE(CTransaction(mtransfertx2).IsNull()); + EXPECT_TRUE(eval.AddTx(mtransfertx2)); + CAmount bal2 = TokenV2Balance(pk2, tokenid); + EXPECT_TRUE(bal2 == 1); + +} + +// test burn pk +TEST_F(TestAssetsCC, tokenv2burn) +{ + struct CCcontract_info *cpTokens, C; + cpTokens = CCinit(&C, TokensV2::EvalCode()); + + eval.SetCurrentHeight(111); + + UniValue tokeldata(UniValue::VOBJ); + tokeldata.pushKV("id", 1); + CMutableTransaction mcreatetx = MakeTokenV2CreateTx(pk1, 1, tokeldata); // burning tokens works only if there is extra data in tokencreate + ASSERT_FALSE(CTransaction(mcreatetx).IsNull()); + EXPECT_TRUE(eval.AddTx(mcreatetx)); + uint256 tokenid = mcreatetx.GetHash(); + + std::vector dests = { pubkey2pk(ParseHex(CC_BURNPUBKEY_FIXED)) }; + + std::vector tokenaddrs = GetTokenV2IndexKeys(pk1); + CMutableTransaction mburntx = MakeTokenV2TransferTx(pk1, 0, tokenid, tokenaddrs, {}, 1, dests, 1, false, true); + ASSERT_FALSE(CTransaction(mburntx).IsNull()); + EXPECT_TRUE(eval.TryAddTx(mburntx)); +} + +// test CCupgrade frameworks +TEST_F(TestAssetsCC, ccupgrade_test) +{ + // init for a fictious chain + strcpy(ASSETCHAINS_SYMBOL, "TESTASSETSCHAIN9876543210"); + CCUpgrades::InitUpgrade(ASSETCHAINS_SYMBOL, 170009); + const int32_t nUpg1Height = 111; + const int64_t nUpg2Time = 1652932911LL; // 19 May 2022; + CCUpgrades::AddUpgradeActive(ASSETCHAINS_SYMBOL, CCUpgrades::CCUPGID_ASSETS_OPDROP_VALIDATE_FIX, nUpg1Height, 170010); + CCUpgrades::AddUpgradeActive(ASSETCHAINS_SYMBOL, CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1, nUpg2Time, 170011); + + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); + EXPECT_FALSE(CCUpgrades::IsUpgradeActive(1652932911LL, nUpg1Height-1, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_ASSETS_OPDROP_VALIDATE_FIX)); + EXPECT_TRUE(CCUpgrades::IsUpgradeActive(1652932911LL, nUpg1Height, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_ASSETS_OPDROP_VALIDATE_FIX)); + + EXPECT_FALSE(CCUpgrades::IsUpgradeActive(nUpg2Time-1, nUpg1Height+100, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)); + EXPECT_TRUE(CCUpgrades::IsUpgradeActive(nUpg2Time, nUpg1Height+100, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)); + + EXPECT_EQ(GetCurrentUpgradeInfo(nUpg2Time-1, nUpg1Height-1, CCUpgrades::GetUpgrades()).nProtocolVersion, 170009); + EXPECT_EQ(GetCurrentUpgradeInfo(nUpg2Time-1, nUpg1Height, CCUpgrades::GetUpgrades()).nProtocolVersion, 170010); + EXPECT_EQ(GetCurrentUpgradeInfo(nUpg2Time, nUpg1Height, CCUpgrades::GetUpgrades()).nProtocolVersion, 170011); +} + + +TEST_F(TestAssetsCC, ccupgrade_tokel) +{ + // TOKEL already initialized in CCUpgrades.cpp + strcpy(ASSETCHAINS_SYMBOL, "TOKEL"); + CCUpgrades::SelectUpgrades(ASSETCHAINS_SYMBOL); + + const int64_t nTime = 1652932911LL; // 19 May 2022; + EXPECT_FALSE(CCUpgrades::IsUpgradeActive(nTime, CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT-1, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_ASSETS_OPDROP_VALIDATE_FIX)); + EXPECT_TRUE(CCUpgrades::IsUpgradeActive(nTime, CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_ASSETS_OPDROP_VALIDATE_FIX)); + + EXPECT_FALSE(CCUpgrades::IsUpgradeActive(CCUpgrades::CCMIXEDMODE_SUBVER_1_TOKEL_TIMESTAMP-1, CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT+100, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)); + EXPECT_TRUE(CCUpgrades::IsUpgradeActive(CCUpgrades::CCMIXEDMODE_SUBVER_1_TOKEL_TIMESTAMP, CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT+100, CCUpgrades::GetUpgrades(), CCUpgrades::CCUPGID_MIXEDMODE_SUBVER_1)); + + EXPECT_EQ(GetCurrentUpgradeInfo(nTime, CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT-1, CCUpgrades::GetUpgrades()).nProtocolVersion, CCUpgrades::CCOLDDEFAULT_PROTOCOL_VERSION); + EXPECT_EQ(GetCurrentUpgradeInfo(CCUpgrades::CCMIXEDMODE_SUBVER_1_TOKEL_TIMESTAMP-1, CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT, CCUpgrades::GetUpgrades()).nProtocolVersion, CCUpgrades::CCOLDDEFAULT_PROTOCOL_VERSION); // we did not change the protocol ver at this moment yet + EXPECT_EQ(GetCurrentUpgradeInfo(CCUpgrades::CCMIXEDMODE_SUBVER_1_TOKEL_TIMESTAMP, CCUpgrades::CCASSETS_OPDROP_FIX_TOKEL_HEIGHT+1, CCUpgrades::GetUpgrades()).nProtocolVersion, CCUpgrades::CCMIXEDMODE_SUBVER_1_PROTOCOL_VERSION); +} + +} /* namespace CCAssetsTests */ diff --git a/src/test-komodo-cc/test-main.cpp b/src/test-komodo-cc/test-main.cpp new file mode 100644 index 00000000000..02da2b45cb2 --- /dev/null +++ b/src/test-komodo-cc/test-main.cpp @@ -0,0 +1,22 @@ +#include "key.h" +#include "base58.h" +#include "chainparams.h" +#include "gtest/gtest.h" +#include "crypto/common.h" + + +int main(int argc, char **argv) { + std::cerr << "testing cc modules consensus code..." << std::endl; + assert(init_and_check_sodium() != -1); + ECC_Start(); + ECCVerifyHandle handle; // Inits secp256k1 verify context + SelectParams(CBaseChainParams::REGTEST); + + CBitcoinSecret vchSecret; + // this returns false due to network prefix mismatch but works anyway + //vchSecret.SetString(notarySecret); + //notaryKey = vchSecret.GetKey(); + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/test-komodo/test_addrman.cpp b/src/test-komodo/test_addrman.cpp index f283b971090..8518200a064 100644 --- a/src/test-komodo/test_addrman.cpp +++ b/src/test-komodo/test_addrman.cpp @@ -68,6 +68,7 @@ class CAddrManTest : public CAddrMan explicit CAddrManTest(bool makeDeterministic = true, std::vector asmap = std::vector()) { + Clear(); if (makeDeterministic) { // Set addrman addr placement to be deterministic. MakeDeterministic(); diff --git a/src/test-komodo/test_coinimport.cpp b/src/test-komodo/test_coinimport.cpp index a0f92d8cae3..9ce2e5f3564 100644 --- a/src/test-komodo/test_coinimport.cpp +++ b/src/test-komodo/test_coinimport.cpp @@ -71,7 +71,7 @@ class TestCoinImport : public ::testing::Test, public Eval { { CTransaction importTx(mtx); PrecomputedTransactionData txdata(importTx); - ServerTransactionSignatureChecker checker(&importTx, 0, 0, false, NULL, txdata); + ServerTransactionSignatureChecker checker(&importTx, 0, 0, false, 0, 1, NULL, txdata); CValidationState verifystate; if (!VerifyCoinImport(importTx.vin[0].scriptSig, checker, verifystate)) printf("TestRunCCEval: %s\n", verifystate.GetRejectReason().data()); @@ -251,7 +251,7 @@ TEST_F(TestCoinImport, DISABLED_testMomomCheckFail) TEST_F(TestCoinImport, testGetCoinImportValue) { - ASSERT_EQ(100, GetCoinImportValue(importTx)); + ASSERT_EQ(100, GetCoinImportValue(importTx, 0, 1)); } } /* namespace TestCoinImport */ diff --git a/src/test-komodo/test_cryptoconditions.cpp b/src/test-komodo/test_cryptoconditions.cpp index 050b0ea7943..c31fb0b4b2e 100644 --- a/src/test-komodo/test_cryptoconditions.cpp +++ b/src/test-komodo/test_cryptoconditions.cpp @@ -79,7 +79,7 @@ TEST_F(CCTest, testMayAcceptCryptoCondition) { "type": "eval-sha-256", "code": "" } ] })!!"); - ASSERT_FALSE(CCPubKey(cond).MayAcceptCryptoCondition()); + ASSERT_TRUE(CCPubKey(cond).MayAcceptCryptoCondition()); // used to be ASSERT_FALSE, now enabled for generic evals } @@ -88,7 +88,7 @@ static bool CCVerify(const CMutableTransaction &mtxTo, const CC *cond) { ScriptError error; CTransaction txTo(mtxTo); PrecomputedTransactionData txdata(txTo); - auto checker = ServerTransactionSignatureChecker(&txTo, 0, amount, false, NULL, txdata); + auto checker = ServerTransactionSignatureChecker(&txTo, 0, amount, false, 0, 1, NULL, txdata); return VerifyScript(CCSig(cond), CCPubKey(cond), 0, checker, 0, &error); }; diff --git a/src/test-komodo/test_eval_bet.cpp b/src/test-komodo/test_eval_bet.cpp index b527c63765f..7c3830cb5f7 100644 --- a/src/test-komodo/test_eval_bet.cpp +++ b/src/test-komodo/test_eval_bet.cpp @@ -15,6 +15,7 @@ extern Eval* EVAL_TEST; +extern int32_t KOMODO_CONNECTING; namespace TestBet { @@ -43,7 +44,7 @@ int TestCC(CMutableTransaction &mtxTo, unsigned int nIn, CC *cond) ScriptError error; CTransaction txTo(mtxTo); PrecomputedTransactionData txdata(txTo); - auto checker = ServerTransactionSignatureChecker(&txTo, nIn, amount, false, NULL, txdata); + auto checker = ServerTransactionSignatureChecker(&txTo, nIn, amount, false, 0, 1, NULL, txdata); return VerifyScript(txTo.vin[nIn].scriptSig, CCPubKey(cond), 0, checker, 0, &error); } @@ -311,9 +312,10 @@ TEST_F(TestBet, testSignDisputeCond) EXPECT_EQ(1, cc_isFulfilled(disputeCond)); } - -TEST_F(TestBet, testDispute) +// these tests do not work in the existing ProcessCC implementation (it does not allow a eval param in the cond->code) +TEST_F(TestBet, DISABLED_testDispute) { + KOMODO_CONNECTING = 1; EvalMock eval = ebet.SetEvalMock(12); // Only one key needed to dispute @@ -332,8 +334,7 @@ TEST_F(TestBet, testDispute) EXPECT_EQ("wrong-payout", eval.state.GetRejectReason()); } - -TEST_F(TestBet, testDisputeInvalidOutput) +TEST_F(TestBet, DISABLED_testDisputeInvalidOutput) { EvalMock eval = ebet.SetEvalMock(11); @@ -356,7 +357,7 @@ TEST_F(TestBet, testDisputeInvalidOutput) } -TEST_F(TestBet, testDisputeEarly) +TEST_F(TestBet, DISABLED_testDisputeEarly) { EvalMock eval = ebet.SetEvalMock(11); @@ -370,7 +371,7 @@ TEST_F(TestBet, testDisputeEarly) } -TEST_F(TestBet, testDisputeInvalidParams) +TEST_F(TestBet, DISABLED_testDisputeInvalidParams) { EvalMock eval = ebet.SetEvalMock(12); @@ -399,7 +400,7 @@ TEST_F(TestBet, testDisputeInvalidParams) } -TEST_F(TestBet, testDisputeInvalidEvidence) +TEST_F(TestBet, DISABLED_testDisputeInvalidEvidence) { EvalMock eval = ebet.SetEvalMock(12); diff --git a/src/test-komodo/test_script_standard_tests.cpp b/src/test-komodo/test_script_standard_tests.cpp index ea08e017c0f..75918c3312d 100644 --- a/src/test-komodo/test_script_standard_tests.cpp +++ b/src/test-komodo/test_script_standard_tests.cpp @@ -38,6 +38,9 @@ namespace TestScriptStandartTests { case TX_CRYPTOCONDITION: res = "TX_CRYPTOCONDITION"; break; + case TX_CLTV: + res = "TX_CLTV"; + break; default: res = "UNKNOWN"; } @@ -341,6 +344,31 @@ namespace TestScriptStandartTests { ASSERT_TRUE(result == expected); } + // timelocked script in tokel repo + TEST(TestScriptStandartTests, script_standard_GetScriptFor_CLTV) { + + CKey keys[1]; + CPubKey pubkeys[1]; + for (int i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CScript expected, result; + + // CCLTVID + int64_t utm = 1651073436; + expected.clear(); + expected << CScriptNum::serialize(utm) << OP_CHECKLOCKTIMEVERIFY << OP_DROP << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + result = GetScriptForDestination(CCLTVID(pubkeys[0], utm)); + ASSERT_TRUE(result == expected); + + expected.clear(); + expected << CScriptNum::serialize(utm) << OP_CHECKLOCKTIMEVERIFY << OP_DROP << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + result = GetScriptForDestination(CCLTVID(pubkeys[0].GetID(), utm)); + ASSERT_TRUE(result == expected); + } + TEST(TestScriptStandartTests, script_standard_IsMine) { CKey keys[2]; diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index dc795ad7a7b..10bc48826d9 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -899,7 +899,7 @@ BOOST_AUTO_TEST_CASE(coins_coinbase_spends) { CTransaction tx2(mtx2); - BOOST_CHECK(Consensus::CheckTxInputs(tx2, state, cache, 100+COINBASE_MATURITY, Params().GetConsensus())); + BOOST_CHECK(Consensus::CheckTxInputs(tx2, state, cache, 100+COINBASE_MATURITY, 0, Params().GetConsensus())); } mtx2.vout.resize(1); @@ -908,7 +908,7 @@ BOOST_AUTO_TEST_CASE(coins_coinbase_spends) { CTransaction tx2(mtx2); - BOOST_CHECK(!Consensus::CheckTxInputs(tx2, state, cache, 100+COINBASE_MATURITY, Params().GetConsensus())); + BOOST_CHECK(!Consensus::CheckTxInputs(tx2, state, cache, 100+COINBASE_MATURITY, 0, Params().GetConsensus())); BOOST_CHECK(state.GetRejectReason() == "bad-txns-coinbase-spend-has-transparent-outputs"); } } diff --git a/src/tui/tui_assets_orders.py b/src/tui/tui_assets_orders.py index d5024e571e8..1dc4951ea80 100644 --- a/src/tui/tui_assets_orders.py +++ b/src/tui/tui_assets_orders.py @@ -5,12 +5,38 @@ import time import json from slickrpc.exc import RpcException +import configparser # test creates fungible and non-fungible tokens # performs transfers and multiple transfers # then runs assets cc ask/bids tests header = "komodo assets cc tokenask/bid v2 test\n" +tui_config_name = './tui_assets_orders.ini' + +def wait_until_confirmed(rpc, txid) : + for i in range(60) : + try : + txdecoded = rpc.getrawtransaction(txid, 1) + # print('get.confirmations', txdecoded.get('confirmations'), type(txdecoded.get('confirmations'))) + if not txdecoded.get('confirmations') is None : + print('confirmations', int(txdecoded.get('confirmations'))) + if int(txdecoded.get('confirmations')) > 0 : + return + except : + print('getrawtransaction exception') + pass + time.sleep(10) + print('still waiting for confirmation...') + +def get_my_address(rpc) : + gr = rpc.listaddressgroupings() + total = 0 + for l1 in gr : + for l2 in l1 : + for addr in l2 : + return addr + return "" def get_result_error(r): if isinstance(r, dict) : @@ -18,7 +44,8 @@ def get_result_error(r): return r.get('error') return '' -def check_tx(r): +# check if result is valid tx +def check_tx_result(r): if isinstance(r, str) : # print("r is str, true") return True @@ -28,11 +55,12 @@ def check_tx(r): if isinstance(str_hex, str) : # print("r has hex, true") if str_hex.find('coins') >= 0 or str_hex.find('tokens') >= 0 or str_hex.find('assets') >= 0 : # try to detect old code returning no coins err as success + print("bad tx result:", r) return False else : return True if r.get('result') == "error" : - # print("r has error, false") + print("bad tx result:", r) return False # print("r has unknown, true") return True @@ -43,80 +71,136 @@ def check_txid(txid) : return True return False -def run_tokens_create(rpc): +def run_tokens_create(rpc1, rpc2, rpc3): # set your own two node params # DIMXY20 - rpc1 = rpclib.rpc_connect("user2135429985", "passe26e9bce922bb0005fff3c41c20e7ea033399104aab3f148c515a2fa72fa4a9272", 14723) - rpc2 = rpclib.rpc_connect("user3701990598", "pass6df4dc57b2ee49b9e591ac6c8cb6aa89f0a06056ce67c6c45bbb14c0d63170e8a0", 15723) - rpc3 = rpclib.rpc_connect("user472219135", "passf6651258f69a92af554a7976ba44707db8eeb2372e6bb89e5557b8d7ee906eecbc", 16723) - - - for v in ["", "v2"] : - # for v in ["v2"] : + # rpc1 = rpclib.rpc_connect("user3088995989", "pass53c05895f37aa1eda9b0de63944275668e1956abb8c38f186132262fe53ae9be7c", 14723) + # rpc2 = rpclib.rpc_connect("user2898668153", "passe99167496d2bbe43876e60430d52eb37b547fa1085b741a15776454eb126d9a603", 15723) + # rpc3 = rpclib.rpc_connect("user972794450", "passe7eb16f5c015a53463cc5f27a004854cb76f4ec5c9aece177f01d8b3d13119e445", 16723) + + # DIMXY29 + #rpc1 = rpclib.rpc_connect("user977812407", "pass45a4a6ce9ffafbebd2a478d3858d55c8a4bded74632bb488c8c93164d048237516", 14723) + #rpc2 = rpclib.rpc_connect("user4228621419", "pass4e51de899bd15c12569d762ded036e5aecd5aeb7be40788a339e72e1fea7359d7e", 15723) + #rpc3 = rpclib.rpc_connect("user3877003389", "passea3e5a6c7a8a88f5447cc39bc8720cf783bbcef43345b16be9aadc0a1b381006eb", 16723) + + ## for v in ["", "v2"] : + ## for v in [""] : + for v in ["v2"] : print("creating fungible token 1...") result = call_rpc(rpc1, "token"+v+"create", "T1", str(0.000001)) # 100 - assert(check_tx(result)) + assert(check_tx_result(result)) tokenid1 = rpc1.sendrawtransaction(result['hex']) print("created token:", tokenid1) - + # wait_until_confirmed(rpc1, tokenid1) + print("creating fungible token 2...") result = call_rpc(rpc1, "token"+v+"create", "T2", str(0.1)) - assert(check_tx(result)) + assert(check_tx_result(result)) tokenid2 = rpc1.sendrawtransaction(result['hex']) print("created token:", tokenid2) + # wait_until_confirmed(rpc1, tokenid2) - print("creating NFT 1 with 00...") - result = call_rpc(rpc1, "token"+v+"create", "NFT-00-1", str(0.00000001), "nft eval 00", "00010203") - assert(check_tx(result)) + print("creating tokel NFT 1 with empty data...") + result = call_rpc(rpc1, "token"+v+"createtokel", "NFT-00-1", str(0.00000001), "nft tokel empty", '{}') + assert(check_tx_result(result)) nft00id1 = rpc1.sendrawtransaction(result['hex']) print("created token:", nft00id1) + # wait_until_confirmed(rpc1, nft00id1) - print("creating NFT 2 with 00...") - result = call_rpc(rpc1, "token"+v+"create", "NFT-00-2", str(0.00000001), "nft eval 00", "00010203") - assert(check_tx(result)) + print("creating tokel NFT 2 with arbitrary data...") + result = call_rpc(rpc1, "token"+v+"createtokel", "NFT-00-2", str(0.00000001), "nft tokel arbitrary", '{"arbitrary":"010203"}') + assert(check_tx_result(result)) nft00id2 = rpc1.sendrawtransaction(result['hex']) print("created token:", nft00id2) + # wait_until_confirmed(rpc1, nft00id2) # tokel nft data F7 evalcode - print("creating NFT with F7, no royalty, with arbitary data...") - result = call_rpc(rpc1, "token"+v+"create", "NFT-F7-1", str(0.00000001), "nft eval=f7 arbitrary=hello", "F70101ee020d687474703a2f2f6d792e6f7267040568656c6c6f") - assert(check_tx(result)) + print("creating tokel NFT with royalty 0...") + result = call_rpc(rpc1, "token"+v+"createtokel", "NFT-F7-1", str(0.00000001), "nft tokel royalty=0", '{"royalty":0}') + assert(check_tx_result(result)) nftf7id1 = rpc1.sendrawtransaction(result['hex']) print("created token:", nftf7id1) + # wait_until_confirmed(rpc1, nftf7id1) - print("creating NFT with F7 and royalty 0xAA...") - result = call_rpc(rpc1, "token"+v+"create", "NFT-F7-2", str(0.00000001), "nft eval=f7 roaylty=AA", "F70101ee020d687474703a2f2f6d792e6f726703AA") - assert(check_tx(result)) - nftf7id2 = rpc1.sendrawtransaction(result['hex']) + print("creating tokel NFT with royalty 999 on rpc3...") + result = call_rpc(rpc3, "token"+v+"createtokel", "NFT-F7-2", str(0.00000001), "nft tokel royalty=999", '{"royalty":999}') + assert(check_tx_result(result)) + nftf7id2 = rpc3.sendrawtransaction(result['hex']) print("created token:", nftf7id2) - + # wait_until_confirmed(rpc1, nftf7id2) + + print("creating tokel NFT with royalty 1 on rpc3 (to test dust)...") + result = call_rpc(rpc3, "token"+v+"createtokel", "NFT-F7-3", str(0.00000001), "nft tokel royalty=1", '{"royalty":1}') + assert(check_tx_result(result)) + nftf7id3 = rpc3.sendrawtransaction(result['hex']) + print("created token:", nftf7id3) + # wait_until_confirmed(rpc1, nftf7id3) + + print("creating tokel NFT with royalty 500 (to test dust)...") + result = call_rpc(rpc3, "token"+v+"createtokel", "NFT-F7-4", str(0.00000001), "nft tokel royalty=500", '{"royalty":500}') + assert(check_tx_result(result)) + nftf7id4 = rpc3.sendrawtransaction(result['hex']) + print("created token:", nftf7id4) + # wait_until_confirmed(rpc1, nftf7id4) + + print("tokens for tests created okay!") + + # transfer tokens from rpc3 to rpc1 (to make a different pubkey than tokencreator) + pubkey1 = rpc1.getinfo()['pubkey'] + call_token_rpc_send_tx(rpc3, "token"+v+"transfer", '', nftf7id2, pubkey1, str(1)) + call_token_rpc_send_tx(rpc3, "token"+v+"transfer", '', nftf7id3, pubkey1, str(1)) + call_token_rpc_send_tx(rpc3, "token"+v+"transfer", '', nftf7id4, pubkey1, str(1)) + + # test tokenlist: + check_tokenlist(rpc1, v, [tokenid1, tokenid2, nft00id1, nft00id2, nftf7id1, nftf7id2, nftf7id3, nftf7id4]) + # test tokenallbalances: + check_tokenallbalances(rpc1, v, {tokenid1: 100, tokenid2: 1000_0000, nft00id1: 1, nft00id2: 1, nftf7id1: 1, nftf7id2: 1, nftf7id3: 1, nftf7id4: 1} ) + # first try transfer tokens to a pk and back, then run assets tests print("starting transfer tests for tokenid version=" + v + "...") run_transfers(rpc1, rpc2, v, tokenid1, tokenid2, 10) + + print("starting transfer tests for nft00id version=" + v + "...") run_transfers(rpc1, rpc2, v, nft00id1, nft00id2, 1) print("starting transfer tests for nftf7id version=" + v + "...") run_transfers(rpc1, rpc2, v, nftf7id1, nftf7id2, 1) - print("token transfers tests finished okay") + print("token transfers tests finished okay!") time.sleep(3) + + # assets cc tests: print("starting assets tests for tokenid1 version=" + v + "...") - run_assets_orders(rpc1, rpc2, v, tokenid1, 10, 8, False) + run_assets_orders(rpc1, rpc2, v, tokenid1, 10, 8, 0.0001, False) print("starting assets tests for nft00id1 version=" + v + "...") - run_assets_orders(rpc1, rpc2, v, nft00id1, 1, 1, True) + run_assets_orders(rpc1, rpc2, v, nft00id1, 1, 1, 0.0001, True) print("starting assets tests for nftf7id1 version=" + v + "...") - run_assets_orders(rpc1, rpc2, v, nftf7id1, 1, 1, True) - - print("starting MofN tests for tokenid1...") - run_MofN_transfers(rpc1, rpc2, rpc3, tokenid1, 10) - print("starting MofN tests for nft00id1...") - run_MofN_transfers(rpc1, rpc2, rpc3, nft00id1, 1) - print("starting MofN tests for nftf7id1...") - run_MofN_transfers(rpc1, rpc2, rpc3, nftf7id1, 1) + run_assets_orders(rpc1, rpc2, v, nftf7id1, 1, 1, 0.0001, True) + print("starting assets tests for nftf7id2 version=" + v + "...") + run_assets_orders(rpc1, rpc2, v, nftf7id2, 1, 1, 0.0001, True) + print("starting assets tests for nftf7id3 version=" + v + "...") + run_assets_orders(rpc1, rpc2, v, nftf7id3, 1, 1, 0.0001, True) + print("starting assets tests for nftf7id4 with unit price creating dust royalty and checking that round correct, version=" + v + "...") + run_assets_orders(rpc1, rpc2, v, nftf7id4, 1, 1, 0.0000_1001, True) + print("starting assets order expiration tests for tokenid1 version=" + v + "...") + run_assets_expired_orders(rpc1, rpc2, v, tokenid1, 10, 8, 0.0001, False) + print("assets tests finished okay!") + + if v == "v2" : # MofN supported for tokens cc v2 only + print("running MofN tests for tokens v2:") + print("starting MofN tests for tokenid1...") + run_MofN_transfers(rpc1, rpc2, rpc3, tokenid1, 10) + print("starting MofN tests for nft00id1...") + run_MofN_transfers(rpc1, rpc2, rpc3, nft00id1, 1) + print("starting MofN tests for nftf7id1...") + run_MofN_transfers(rpc1, rpc2, rpc3, nftf7id1, 1) + print("token MofN transfer tests finished okay!") + + - print("assets tests finished okay") + print("all token/assets tests finished okay!") time.sleep(3) exit @@ -132,7 +216,7 @@ def call_token_rpc_create_tx(rpc, rpcname, stop_error, *args) : print("calling " + rpcname) result = rpcfunc(*args) print(rpcname + " create tx result:", result, " arg=", args[0]) - if check_tx(result): + if check_tx_result(result): break if stop_error and get_result_error(result) == stop_error : print(rpcname + " retrying stopped because of expected stop error received: " + stop_error) @@ -140,7 +224,7 @@ def call_token_rpc_create_tx(rpc, rpcname, stop_error, *args) : if i < retries-1: print("retrying " + rpcname + '...') time.sleep(delay) - assert(check_tx(result)) + assert(check_tx_result(result)) print(rpcname + " tx created") return result @@ -148,16 +232,16 @@ def call_token_rpc_send_tx(rpc, rpcname, stop_error, *args) : for i in range(3) : result = call_token_rpc_create_tx(rpc, rpcname, stop_error, *args) - if check_tx(result) : + if check_tx_result(result) : try : txid = rpc.sendrawtransaction(result['hex']) print("sendrawtransaction result: ", txid) - assert(check_tx(txid)) + assert(check_tx_result(txid)) print(rpcname + " tx sent") return txid except RpcException as e : - if e.message.find('replacement in mempool') or e.message.find('missing inputs') : - print('double spending in mempool - retrying...') + if e.message.find('replacement in mempool') >= 0 or e.message.find('missing inputs') >= 0 : + print('double spending in mempool', e.message, 'retrying...') pass else : assert False, e @@ -166,14 +250,14 @@ def call_token_rpc_send_tx(rpc, rpcname, stop_error, *args) : assert False, 'sendrawtransaction no more retries' def call_rpc_retry(rpc, rpcname, stop_error, *args) : - retries = 24 + retries = 36 delay = 10 rpcfunc = getattr(rpc, rpcname) for i in range(retries): print("calling " + rpcname) result = rpcfunc(*args) - print(rpcname + " result:", result) - if check_tx(result): + # print(rpcname + " result:", result) + if check_tx_result(result): break if stop_error and get_result_error(result) == stop_error : print(rpcname + " retrying stopped because of expected stop error received: " + stop_error) @@ -181,12 +265,13 @@ def call_rpc_retry(rpc, rpcname, stop_error, *args) : if i < retries-1: print("retrying " + rpcname + '...') time.sleep(delay) - assert(check_tx(result)) - print(rpcname + " tx created") + assert(check_tx_result(result)) + # print(rpcname + " finished okay") return result def call_token_rpc(rpc, rpcname, stop_error, *args) : - print("calling " + rpcname + " for tokenid=", args[0]) + if len(args) > 0 : + print("calling " + rpcname + " for tokenid=", args[0]) return call_rpc_retry(rpc, rpcname, stop_error, *args) def run_transfers(rpc1, rpc2, v, tokenid1, tokenid2, amount): @@ -242,8 +327,8 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): param["destpubkeys"] = [ pubkey1, pubkey2 ] param["M"] = 1 param["amount"] = amount - print("creating tokenv2transfer tokenid amount tx to 1of2 tx...", json.dumps(param)) - tx1of2 = call_token_rpc_create_tx(rpc1, "tokenv2transfer", '', json.dumps(param)) + print("creating tokenv2transferMofN tokenid amount tx to 1of2 tx...", json.dumps(param)) + tx1of2 = call_token_rpc_create_tx(rpc1, "tokenv2transferMofN", '', json.dumps(param)) txid = rpc1.sendrawtransaction(tx1of2['hex']) print("sendrawtransaction result: ", txid) assert(check_txid(txid)) @@ -260,23 +345,22 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): if amount > 1 : # if not nft first send half to pk2 (to test lib cc fulfilment ordering) # try firs1 to pk2 param["destpubkeys"] = [ pubkey2 ] - print("creating tokenv2transfer tokenid1 tx spending 1of2 back to pk2...", json.dumps(param)) - txid = call_token_rpc_send_tx(rpc2, "tokenv2transfer", '', json.dumps(param)) + print("creating tokenv2transferMofN tokenid1 tx spending 1of2 back to pk2...", json.dumps(param)) + txid = call_token_rpc_send_tx(rpc2, "tokenv2transferMofN", '', json.dumps(param)) assert(check_txid(txid)) - else : # if nft send 1 back to pk1 for the next test param["destpubkeys"] = [ pubkey1 ] - print("creating tokenv2transfer tokenid tx spending 1of2 back to pk1...", json.dumps(param)) - txid = call_token_rpc_send_tx(rpc1, "tokenv2transfer", '', json.dumps(param)) + print("creating tokenv2transferMofN tokenid tx spending 1of2 back to pk1...", json.dumps(param)) + txid = call_token_rpc_send_tx(rpc1, "tokenv2transferMofN", '', json.dumps(param)) assert(check_txid(txid)) if amount > 1 : # if not nft send half amount back to pk1 # wait to prevent adding same inputs while tx is not propagated to this node mempool time.sleep(3) param["destpubkeys"] = [ pubkey1 ] - print("creating tokenv2transfer tokenid tx spending 1of2 back to pk1...",json.dumps(param)) - txid = call_token_rpc_send_tx(rpc1, "tokenv2transfer", '', json.dumps(param)) + print("creating tokenv2transferMofN tokenid tx spending 1of2 back to pk1...",json.dumps(param)) + txid = call_token_rpc_send_tx(rpc1, "tokenv2transferMofN", '', json.dumps(param)) assert(check_txid(txid)) time.sleep(1) # pause to allow tx in mempool to propagate to prevent double spending in mempool @@ -287,8 +371,8 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): param["destpubkeys"] = [ pubkey1, pubkey2 ] param["M"] = 2 param["amount"] = amount - print("creating tokenv2transfer tokenid amount tx to 2of2 ...", json.dumps(param)) - tx2of2 = call_token_rpc_create_tx(rpc1, "tokenv2transfer", '', json.dumps(param)) + print("creating tokenv2transferMofN tokenid amount tx to 2of2 ...", json.dumps(param)) + tx2of2 = call_token_rpc_create_tx(rpc1, "tokenv2transferMofN", '', json.dumps(param)) txid = rpc1.sendrawtransaction(tx2of2['hex']) print("sendrawtransaction result: ", txid) assert(check_txid(txid)) @@ -303,8 +387,8 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): param["destpubkeys"] = [ pubkey1 ] param["M"] = 1 param["amount"] = amount - print("creating tokenv2transfer tokenid tx spending 2of2 back to pk1...", json.dumps(param)) - partialtx = call_token_rpc(rpc1, "tokenv2transfer", '', json.dumps(param)) + print("creating tokenv2transferMofN tokenid tx spending 2of2 back to pk1...", json.dumps(param)) + partialtx = call_token_rpc(rpc1, "tokenv2transferMofN", '', json.dumps(param)) assert partialtx['hex'], 'partial tx not created' assert partialtx['PartiallySigned'], 'partially signed object' @@ -314,7 +398,7 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): print("sending partially1 signed tx returned:", result) assert not result, 'sending partially signed 2of2 tx should return error' except RpcException as e : - print ('got normal exception', e.message) + print ('got a normal exception (note "FULFILLMENT NOT ENCODED" msg on console is normal)', e.message) pass # should be error tx2of2back = call_rpc_retry(rpc2, "addccv2signature", '', partialtx['hex'], partialtx['PartiallySigned']) @@ -332,8 +416,8 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): param["destpubkeys"] = [ pubkey1, pubkey2, pubkey3 ] param["M"] = 2 param["amount"] = amount - print("creating tokenv2transfer tokenid amount tx to 2of3 tx...", json.dumps(param)) - tx2of3 = call_token_rpc_create_tx(rpc1, "tokenv2transfer", '', json.dumps(param)) + print("creating tokenv2transferMofN tokenid amount tx to 2of3 tx...", json.dumps(param)) + tx2of3 = call_token_rpc_create_tx(rpc1, "tokenv2transferMofN", '', json.dumps(param)) txid = rpc1.sendrawtransaction(tx2of3['hex']) print("sendrawtransaction result: ", txid) assert(check_txid(txid)) @@ -348,8 +432,8 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): param["M"] = 1 param["amount"] = amountback param["destpubkeys"] = [ pubkey1 ] - print("creating tokenv2transfer tokenid tx spending 2of3 back to pk1...", json.dumps(param)) - partialtx = call_token_rpc(rpc1, "tokenv2transfer", '', json.dumps(param)) + print("creating tokenv2transferMofN tokenid tx spending 2of3 back to pk1...", json.dumps(param)) + partialtx = call_token_rpc(rpc1, "tokenv2transferMofN", '', json.dumps(param)) assert partialtx['hex'], 'partial tx not created' assert partialtx['PartiallySigned'], 'partially signed object' @@ -359,7 +443,7 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): print("sending partially1 signed tx returned:", result) assert not result, 'sending partially signed 2of3 tx should return error' except RpcException as e : - print ('got normal exception', e.message) + print ('got a normal exception (note "FULFILLMENT NOT ENCODED" msg on console is normal)', e.message) pass # should be error @@ -379,8 +463,8 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): param["destpubkeys"] = [ pubkey1, pubkey2, pubkey3 ] param["M"] = 3 param["amount"] = amount - print("creating tokenv2transfer tokenid amount tx to 3of3 ...", json.dumps(param)) - tx3of3 = call_token_rpc_create_tx(rpc1, "tokenv2transfer", '', json.dumps(param)) + print("creating tokenv2transferMofN tokenid amount tx to 3of3 ...", json.dumps(param)) + tx3of3 = call_token_rpc_create_tx(rpc1, "tokenv2transferMofN", '', json.dumps(param)) txid = rpc1.sendrawtransaction(tx3of3['hex']) print("sendrawtransaction result: ", txid) assert(check_txid(txid)) @@ -395,8 +479,8 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): param["destpubkeys"] = [ pubkey1 ] param["M"] = 1 param["amount"] = amount - print("creating tokenv2transfer tokenid tx spending 3of3 back to pk1...", json.dumps(param)) - partialtx1 = call_token_rpc(rpc1, "tokenv2transfer", '', json.dumps(param)) + print("creating tokenv2transferMofN tokenid tx spending 3of3 back to pk1...", json.dumps(param)) + partialtx1 = call_token_rpc(rpc1, "tokenv2transferMofN", '', json.dumps(param)) assert partialtx1['hex'], 'partial tx not created' assert partialtx1['PartiallySigned'], 'partially signed object' @@ -406,7 +490,7 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): print("sending partially1 signed tx returned:", result) assert not result, 'sending partially signed 3of3 tx should return error' except RpcException as e : - print ('got normal exception', e.message) + print ('got a normal exception (note "FULFILLMENT NOT ENCODED" msg on console is normal)', e.message) pass # should be error # add sig 2 @@ -419,7 +503,8 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): result = rpc2.sendrawtransaction(partialtx2['hex']) print("sending partially2 signed tx returned:", result) assert not result, 'sending partially signed 3of3 tx should return error' - except (RpcException) : + except RpcException as e : + print ('got a normal exception (note "FULFILLMENT NOT ENCODED" msg on console is normal)', e.message) pass # should be error # add sig 3 @@ -432,13 +517,13 @@ def run_MofN_transfers(rpc1, rpc2, rpc3, tokenid, amount): print("tx 3of3 back sent") -def run_assets_orders(rpc1, rpc2, v, tokenid, total, units, isnft): +def run_assets_orders(rpc1, rpc2, v, tokenid, total, units, unitprice, isnft): retries = 24 delay = 10 askunits = bidunits = units - unitprice = 0.0001 + # unitprice = 0.0001 # wait for mempool to empty to get correct balance: retries = 24 @@ -454,7 +539,7 @@ def run_assets_orders(rpc1, rpc2, v, tokenid, total, units, isnft): # get initial balance result = call_rpc(rpc1, "token"+v+"balance", tokenid) - assert(check_tx(result)) + assert(check_tx_result(result)) initial_balance = int(result["balance"]) print("initial balance for tokenid " + tokenid + " = " + str(initial_balance)) @@ -520,13 +605,35 @@ def run_assets_orders(rpc1, rpc2, v, tokenid, total, units, isnft): print("creating token"+v+"cancelbid tx #3...") cancelid = call_token_rpc_send_tx(rpc1, "token"+v+"cancelbid", 'bid is empty', tokenid, fillbidid3) + # create tokenbid to self to test dust + print("creating token"+v+"bid tx #4...") + bidid4 = call_token_rpc_send_tx(rpc1, "token"+v+"bid", '', str(1), tokenid, str(0.0000_0600)) # price more than dust + + # fill bid to self with dust price + print("creating token"+v+"fillbid tx #4 (to check failure on dust unit price)...") + # enable this if dust is enabled on normals in the chain: + # fillbidid4 = call_token_rpc_send_tx(rpc1, "token"+v+"fillbid", '', tokenid, bidid4, str(1), str(0.0000_005)) # fill with dust price (<=500sat) + # if dust not enabled we should get error: + fillbidid4 = call_token_rpc(rpc1, "token"+v+"fillbid", '', tokenid, bidid4, str(1), str(0.0000_005)) # fill with dust price (<=500sat) + assert fillbidid4['hex'], "token"+v+"fillbid" + ' tx not created' + + try : + print("trying to sendrawtransaction tokenfillbid tx with dust... ") + result = rpc1.sendrawtransaction(fillbidid4['hex']) + print("sendrawtransaction tokenfillbid tx with dust returned:", result) + assert not result, 'sending tokenfillbid tx with dust should return error' + except RpcException as e : + print ('got a normal exception for tokenfillbid tx with dust', e.message) + pass # should be error + # balance does not change as this is a tx to self + # check balance after retries = 24 delay = 10 for i in range(retries): print("calling " + "token"+v+"balance") finresult = call_rpc(rpc1, "token"+v+"balance", tokenid) - assert(check_tx(result)) + assert(check_tx_result(result)) if int(finresult["balance"]) == initial_balance : break if i < retries-1: @@ -535,35 +642,199 @@ def run_assets_orders(rpc1, rpc2, v, tokenid, total, units, isnft): assert(int(finresult["balance"]) == initial_balance) -menuItems = [ - {"run token create/transfers and assets orders": run_tokens_create}, - {"Exit": exit} -] - - -def main(): - while True: - os.system('clear') - print(tuilib.colorize(header, 'pink')) - print(tuilib.colorize('CLI version 0.2\n', 'green')) - for item in menuItems: - print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) - choice = input(">> ") - try: - if int(choice) < 0: - raise ValueError - # Call the matching function - if list(menuItems[int(choice)].keys())[0] == "Exit": - list(menuItems[int(choice)].values())[0]() - else: - list(menuItems[int(choice)].values())[0](rpc_connection) - except (ValueError, IndexError): - pass +def wait_for_height(rpc, ht) : + # wait for expiration height + while True : + time.sleep(30) + getinfo = rpc.getinfo() + h1 = getinfo["blocks"] + if h1 >= ht : + break + print ('waiting for expiration height...') + myaddr = get_my_address(rpc) + rpc.sendtoaddress(myaddr, 0.1) # this is for on demand chains to advance a chain + +# test expiration height in orders +def run_assets_expired_orders(rpc1, rpc2, v, tokenid, total, units, unitprice, isnft): + + askunits = bidunits = units + + getinfo = rpc1.getinfo() + hoff = 5 + h0 = getinfo["blocks"] + + # create tokenask with expiration height + print("creating token"+v+"ask with expiration tx #1...") + askid1 = call_token_rpc_send_tx(rpc1, "token"+v+"ask", '', str(total), tokenid, str(unitprice), str(h0+hoff)) + print("trying to cancel not yet expired order with other pk: token"+v+"cancelask...") + result = call_token_rpc_create_tx(rpc2, "token"+v+"cancelask", 'ask is empty', tokenid, askid1) + assert(check_tx_result(result)) + try : + cancelid = rpc2.sendrawtransaction(result['hex']) + assert(get_result_error(cancelid)) # should return error for not yet expired order + except RpcException as e : + print ('got a normal exception for tokencancelask not expired', e.message) + pass # exception is a normal execution + + # wait for expiration height + wait_for_height(rpc1, h0 + hoff) + + print("trying to fill expired order token"+v+"fillask tx #1...") + result = call_token_rpc_create_tx(rpc2, "token"+v+"fillask", '', tokenid, askid1, str(askunits)) + assert(check_tx_result(result)) + try : + fillaskid1 = rpc2.sendrawtransaction(result['hex']) + print("fillaskid1", fillaskid1) + print("get_result_error(fillaskid1)", get_result_error(fillaskid1)) + assert(get_result_error(fillaskid1)) # should return error for yet expired order + except RpcException as e : + print ('got a normal exception for tokenfillask expired', e.message) + pass # exception is a normal execution + + print("trying to cancel not yet expired order with other pk: token"+v+"cancelask...") + cancelid2 = call_token_rpc_send_tx(rpc2, "token"+v+"cancelask", 'ask is empty', tokenid, askid1) + + # same for tokenbid + getinfo = rpc1.getinfo() + h0 = getinfo["blocks"] + + # create tokenbid with expiration height + print("creating token"+v+"bid with expiration tx #1...") + bidid1 = call_token_rpc_send_tx(rpc2, "token"+v+"bid", '', str(total), tokenid, str(unitprice), str(h0+hoff)) + + print("trying to cancel not yet expired order with other pk: token"+v+"cancelbid...") + result = call_token_rpc_create_tx(rpc1, "token"+v+"cancelbid", 'bid is empty', tokenid, bidid1) + assert(check_tx_result(result)) + try : + cancelid = rpc1.sendrawtransaction(result['hex']) + assert(get_result_error(cancelid)) # should return error for not yet expired order + except RpcException as e : + print ('got a normal exception for tokencancelbid not expired', e.message) + pass # should be exception + + # wait for expiration height + wait_for_height(rpc1, h0 + hoff) + + print("trying to fill expired order token"+v+"fillbid tx #1...") + result = call_token_rpc_create_tx(rpc1, "token"+v+"fillbid", '', tokenid, bidid1, str(bidunits)) + assert(check_tx_result(result)) + try : + fillbidid1 = rpc1.sendrawtransaction(result['hex']) + assert(get_result_error(fillbidid1)) # should return error for yet expired order + except RpcException as e : + print ('got a normal exception for tokenfillbid expired', e.message) + pass # should be exception here + + print("trying to cancel not yet expired order with other pk: token"+v+"cancelbid...") + cancelid3 = call_token_rpc_send_tx(rpc1, "token"+v+"cancelbid", 'bid is empty', tokenid, bidid1) + +def check_tokenlist(rpc, v, tokenids) : + rpcname = "token" + v + "list" + results = {} + retries = 24 + delay = 10 + for i in range(retries) : + tokenlist = call_token_rpc(rpc, rpcname, '') + for tid in tokenids : + if tid in tokenlist : + results[tid] = 1 + if len(results) == len(tokenids) : + break + time.sleep(delay) + print('retrying check_tokenlist waiting for txns to mine...') + + assert len(results) == len(tokenids), "not all tokenids found in " + rpcname + " results" + print('check_tokenlist okay!') + +def check_tokenallbalances(rpc, v, checkbalances) : + rpcname = "token" + v + "allbalances" + results = {} + retries = 24 + delay = 10 + for i in range(retries) : + tokenbalances = call_token_rpc(rpc, rpcname, '') + # print('tokenbalances', tokenbalances) + for i, (checktid, checkamount) in enumerate(checkbalances.items()) : + for tbalance in tokenbalances : + for tid, amount in tbalance.items() : # get tokenid and amount from tokenallbalances items like {'1233937': 100000} + if tid == checktid : + assert amount == checkamount, 'invalid token amount ' + str(amount) + ' returned from ' + rpcname + ' for tokenid ' + checktid + results[tid] = 1 + if len(results) == len(checkbalances) : + break + time.sleep(delay) + print('retrying check_tokenallbalances waiting for txns to mine...') + assert len(results) == len(checkbalances), "not all tokenids have correct amounts in " + rpcname + " results" + print('check_tokenallbalances okay!') + +def get_kmd_rpc(config) : + rpc = None + with open(config) as file: + lines = file.readlines() + + rpcport = 7771 + cookiename = '__cookie__' + cookie = '' + for l in lines : + t = l.split(':') + if len(t) == 2 : + if t[0] == cookiename : + cookie = t[1] + + rpc = rpclib.rpc_connect(cookiename, cookie, int(rpcport)) + return rpc + + +def get_as_chain_rpc(config) : + rpc = None + with open(config) as file: + lines = file.readlines() + rpcuser = '' + rpcpassword = '' + rpcport = 0 + for l in lines : + t = l.split('=') + # print('t=', t) + if len(t) == 2 : + if t[0] == 'rpcuser' : + rpcuser = t[1].strip() + elif t[0] == 'rpcpassword' : + rpcpassword = t[1].strip() + elif t[0] == 'rpcport' : + rpcport = int(t[1]) + + print('rpcuser', rpcuser, 'rpcpassword', rpcpassword, 'rpcport', rpcport) + rpc = rpclib.rpc_connect(rpcuser, rpcpassword, int(rpcport)) + return rpc + +def get_chain_rpc(config) : + + rpc = None + # print('config[-4:]', config[-4:]) + if config[-4:] == 'conf' : + rpc = get_as_chain_rpc(config) + elif config[-6:] == 'cookie' : + rpc = get_kmd_rpc(config) + else : + assert False, 'could not find connection params' + + rpc.getinfo() # try connect + print('rpc connected') + return rpc if __name__ == "__main__": - print("starting assets orders test (remember to set rpc params for two nodes for your chain)") - time.sleep(2) - rpc_connection = "" - main() + print("starting assets orders test") + print("plz first start a chain of three nodes and enter rpc params in the code") + print("ensure you have at least 10 utxos in the nodes' wallets for batch transaction creation") + + rpc1 = get_chain_rpc('/Users/dimxy/Library/Application Support/Komodo/DIMXY31/DIMXY31.conf') + rpc2 = get_chain_rpc('/Users/dimxy/repo/tokel/src/DIMXY31/nodes/1/DIMXY31.conf') + rpc3 = get_chain_rpc('/Users/dimxy/repo/tokel/src/DIMXY31/nodes/2/DIMXY31.conf') + + + # time.sleep(2) + # makeTuiConfig() + # main() + run_tokens_create(rpc1, rpc2, rpc3) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 8947096e699..e4919cdc4d6 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -768,6 +768,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const CCoinsViewCache mempoolDuplicate(const_cast(pcoins)); const int64_t nSpendHeight = GetSpendHeight(mempoolDuplicate); + const int64_t nSpendTime = GetSpendTime(mempoolDuplicate); LOCK(cs); list waitingOnDependants; @@ -829,7 +830,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const else { CValidationState state; bool fCheckResult = tx.IsCoinBase() || - Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight, Params().GetConsensus()); + Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight, nSpendTime, Params().GetConsensus()); assert(fCheckResult); UpdateCoins(tx, mempoolDuplicate, 1000000); } @@ -845,7 +846,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const assert(stepsSinceLastRemove < waitingOnDependants.size()); } else { bool fCheckResult = entry->GetTx().IsCoinBase() || - Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight, Params().GetConsensus()); + Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight, nSpendTime, Params().GetConsensus()); assert(fCheckResult); UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); stepsSinceLastRemove = 0; diff --git a/src/util.cpp b/src/util.cpp index fa62ba1ff85..bee7903d2b5 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -30,8 +30,7 @@ #include "sync.h" #include "utilstrencodings.h" #include "utiltime.h" -#include "komodo_defs.h" - +#include "komodo_defs.h" #include #include #include @@ -394,8 +393,8 @@ void ParseParameters(int argc, const char* const argv[]) } } -// split string using by space or comma as a delimiter char -void SplitStr(const std::string& strVal, std::vector &outVals) +// split string using space, comma (default) or other char (param) as a delimiter +void SplitStr(const std::string& strVal, std::vector &outVals, const std::string &delims) { stringstream ss(strVal); @@ -406,7 +405,7 @@ void SplitStr(const std::string& strVal, std::vector &outVals) while (std::isspace(ss.peek())) ss.ignore(); - while ((c = ss.get()) != EOF && !std::isspace(c) && c != ',') + while ((c = ss.get()) != EOF && !std::isspace(c) && delims.find(c) == std::string::npos) str += c; if (!str.empty()) @@ -414,36 +413,34 @@ void SplitStr(const std::string& strVal, std::vector &outVals) } } -void Split(const std::string& strVal, int32_t outsize, uint64_t *outVals, const uint64_t nDefault) +void SplitIntoU64List(const std::string& strVal, int32_t outsize, uint64_t* outVals, const uint64_t nDefault) { stringstream ss(strVal); vector vec; - uint64_t i, nLast, numVals = 0; + uint64_t val, nLast, numVals = 0; - while ( ss.peek() == ' ' ) + while (ss.peek() == ' ') ss.ignore(); - while ( ss >> i ) - { - outVals[numVals] = i; - numVals += 1; + while (ss >> val && numVals < outsize) { + outVals[numVals] = val; + numVals++; - while ( ss.peek() == ' ' ) + while (ss.peek() == ' ') ss.ignore(); - if ( ss.peek() == ',' ) + if (ss.peek() == ',') ss.ignore(); - while ( ss.peek() == ' ' ) + while (ss.peek() == ' ') ss.ignore(); } - if ( numVals > 0 ) + if (numVals > 0) nLast = outVals[numVals - 1]; else nLast = nDefault; - for ( i = numVals; i < outsize; i++ ) - { + for (int i = numVals; i < outsize; i++) { outVals[i] = nLast; } } @@ -735,7 +732,6 @@ void ReadConfigFile(map& mapSettingsRet, } // If datadir is changed in .conf file: ClearDatadirCache(); - extern uint16_t BITCOIND_RPCPORT; BITCOIND_RPCPORT = GetArg("-rpcport",BaseParams().RPCPort()); } @@ -1040,3 +1036,34 @@ int GetNumCores() return boost::thread::physical_concurrency(); } +// add settings to args maps (to add default opts for a specific chain defined outside) +void AddSettings(std::map& mapSettingsRet, + std::map >& mapMultiSettingsRet, const std::string &strOpts) +{ + std::vector vOpts; + SplitStr(strOpts, vOpts, " "); + for (auto const &opt : vOpts) + { + std::vector namevalue; + SplitStr(opt, namevalue, "="); + if (namevalue.size() != 2) + throw std::runtime_error("Invalid option name-value format in override-settings variable"); + std::string name = namevalue[0]; + std::string value = namevalue[1]; + if (name[0] != '-') + throw std::runtime_error("Invalid option format in override-settings variable"); + + // Interpret --foo as -foo. + // If both --foo and -foo are set, the last takes effect. + if (name.length() > 1 && name[1] == '-') + name = name.substr(1); + + if (mapSettingsRet.count(name) == 0) + { + mapSettingsRet[name] = value; + // interpret nofoo=1 as foo=0 (and nofoo=0 as foo=1) as long as foo not set) + InterpretNegativeSetting(name, mapSettingsRet); + } + mapMultiSettingsRet[name].push_back(value); + } +} \ No newline at end of file diff --git a/src/util.h b/src/util.h index 17bf19952b0..554555d0530 100644 --- a/src/util.h +++ b/src/util.h @@ -179,13 +179,13 @@ inline bool IsSwitchChar(char c) /** * Return string argument or default value * - * @param strVal string to split + * @param strVal string to split into array of uint64_t * @param outVals array of numbers from string or default - * if the string is null, nDefault is used for all array entries - * else if the string has fewer than _MAX_ERAS entries, then the last - * entry fills remaining entries + * if the strVal is null, nDefault is used for all array entries + * else if the strVal has fewer than outsize entries, then the last + * entry fills remaining entries in outVals */ -void Split(const std::string& strVal, int32_t outsize, uint64_t *outVals, uint64_t nDefault); +void SplitIntoU64List(const std::string& strVal, int32_t outsize, uint64_t *outVals, uint64_t nDefault); /** * Return string argument or default value @@ -288,7 +288,8 @@ template void TraceThread(const char* name, Callable func) } // split string using by space or comma as a delimiter char -void SplitStr(const std::string& strVal, std::vector &outVals); +void SplitStr(const std::string& strVal, std::vector &outVals, const std::string &delims = ","); +void AddSettings(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet, const std::string &strOpts); #define KOMODO_ASSETCHAIN_MAXLEN 65 diff --git a/src/version.h b/src/version.h index 81624e423b3..e698a919a8f 100644 --- a/src/version.h +++ b/src/version.h @@ -24,7 +24,7 @@ * network protocol versioning */ -static const int PROTOCOL_VERSION = 170009; +static const int PROTOCOL_VERSION = 170009 + 1; // Tokel HF June 2022 //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; diff --git a/src/wallet-utility.cpp b/src/wallet-utility.cpp index 0b664ecb35a..4972ccd35bb 100644 --- a/src/wallet-utility.cpp +++ b/src/wallet-utility.cpp @@ -9,7 +9,9 @@ #include #include "komodo_defs.h" -char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +#include "komodo_globals.h" + +/*char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; int64_t MAX_MONEY = 200000000 * 100000000LL; uint64_t ASSETCHAINS_SUPPLY; uint16_t BITCOIND_RPCPORT = 7771; @@ -26,7 +28,7 @@ int32_t ASSETCHAINS_OVERWINTER = 227520; int32_t ASSETCHAINS_SAPLING = 227520; int32_t KOMODO_TESTNODE = 0; -unsigned int MAX_BLOCK_SIGOPS = 20000; +unsigned int MAX_BLOCK_SIGOPS = 20000;*/ void show_help() { diff --git a/src/wallet/rpcdisclosure.cpp b/src/wallet/rpcdisclosure.cpp index cd0cc42a6c5..e42628f6416 100644 --- a/src/wallet/rpcdisclosure.cpp +++ b/src/wallet/rpcdisclosure.cpp @@ -42,11 +42,11 @@ #include "zcash/Note.hpp" #include "zcash/NoteEncryption.hpp" +#include "rpcwallet.h" + using namespace std; using namespace libzcash; -// Function declaration for function implemented in wallet/rpcwallet.cpp -bool EnsureWalletIsAvailable(bool avoidException); /** * RPC call to generate a payment disclosure diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 31f583ee4f1..f46d90c49ce 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -37,10 +37,10 @@ #include -using namespace std; +#include "komodo_nSPV_defs.h" +#include "rpcwallet.h" -void EnsureWalletIsUnlocked(); -bool EnsureWalletIsAvailable(bool avoidException); +using namespace std; UniValue dumpwallet_impl(const UniValue& params, bool fHelp, bool fDumpZKeys); UniValue importwallet_impl(const UniValue& params, bool fHelp, bool fImportZKeys); @@ -1008,26 +1008,8 @@ UniValue z_exportviewingkey(const UniValue& params, bool fHelp, const CPubKey& m } extern int32_t KOMODO_NSPV,KOMODO_DEX_P2P; -#ifndef KOMODO_NSPV_FULLNODE -#define KOMODO_NSPV_FULLNODE (KOMODO_NSPV <= 0) -#endif // !KOMODO_NSPV_FULLNODE -#ifndef KOMODO_NSPV_SUPERLITE -#define KOMODO_NSPV_SUPERLITE (KOMODO_NSPV > 0) -#endif // !KOMODO_NSPV_SUPERLITE uint256 zeroid; -UniValue NSPV_getinfo_req(int32_t reqht); -UniValue NSPV_login(char *wifstr); -UniValue NSPV_logout(); -UniValue NSPV_addresstxids(char *coinaddr,int32_t CCflag,int32_t skipcount,int32_t filter); -UniValue NSPV_addressutxos(char *coinaddr,int32_t CCflag,int32_t skipcount,int32_t filter); -UniValue NSPV_mempooltxids(char *coinaddr,int32_t CCflag,uint8_t funcid,uint256 txid,int32_t vout); -UniValue NSPV_broadcast(char *hex); -UniValue NSPV_spend(char *srcaddr,char *destaddr,int64_t satoshis); -UniValue NSPV_spentinfo(uint256 txid,int32_t vout); -UniValue NSPV_notarizations(int32_t height); -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); @@ -1420,14 +1402,13 @@ UniValue nspv_notarizations(const UniValue& params, bool fHelp, const CPubKey& m UniValue nspv_hdrsproof(const UniValue& params, bool fHelp, const CPubKey& mypk) { - int32_t prevheight,nextheight; - if ( fHelp || params.size() != 2 ) - throw runtime_error("nspv_hdrsproof prevheight nextheight\n"); + int32_t nextheight; + if ( fHelp || params.size() != 1 ) + throw runtime_error("nspv_hdrsproof 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()); - return(NSPV_hdrsproof(prevheight,nextheight)); + nextheight = atol((char *)params[0].get_str().c_str()); + return(NSPV_hdrsproof(nextheight)); } UniValue nspv_txproof(const UniValue& params, bool fHelp, const CPubKey& mypk) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 46b7f7a3211..95d356f83ac 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -38,6 +38,7 @@ #include "script/interpreter.h" #include "zcash/zip32.h" #include "notaries_staked.h" +#include "sync_ext.h" #include "utiltime.h" #include "asyncrpcoperation.h" @@ -62,6 +63,8 @@ #include "komodo_defs.h" #include +#include "rpcwallet.h" + #include "../cc/CCfaucet.h" #include "../cc/CCrewards.h" #include "../cc/CCdice.h" @@ -80,16 +83,8 @@ using namespace std; using namespace libzcash; -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -extern std::string ASSETCHAINS_OVERRIDE_PUBKEY; const std::string ADDR_TYPE_SPROUT = "sprout"; const std::string ADDR_TYPE_SAPLING = "sapling"; -extern UniValue TxJoinSplitToJSON(const CTransaction& tx); -extern int32_t KOMODO_INSYNC; -uint32_t komodo_segid32(char *coinaddr); -int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); -int32_t komodo_isnotaryvout(char *coinaddr,uint32_t tiptime); // from ac_private chains only -CBlockIndex *komodo_getblockindex(uint256 hash); int64_t nWalletUnlockTime; static CCriticalSection cs_nWalletUnlockTime; @@ -101,8 +96,6 @@ UniValue z_getoperationstatus_IMPL(const UniValue&, bool); #define VALID_PLAN_NAME(x) (strlen(x) <= PLAN_NAME_MAX) #define THROW_IF_SYNCING(INSYNC) if (INSYNC == 0) { throw runtime_error(strprintf("%s: Chain still syncing at height %d, aborting to prevent linkability analysis!",__FUNCTION__,chainActive.Tip()->GetHeight())); } -int tx_height( const uint256 &hash ); - std::string HelpRequiringPassphrase() { return pwalletMain && pwalletMain->IsCrypted() @@ -190,7 +183,7 @@ char *komodo_chainname() return(ASSETCHAINS_SYMBOL[0] == 0 ? (char *)"KMD" : ASSETCHAINS_SYMBOL); } -void OS_randombytes(unsigned char *x,long xlen); +// void OS_randombytes(unsigned char *x,long xlen); UniValue getnewaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) { @@ -470,7 +463,7 @@ UniValue getaddressesbyaccount(const UniValue& params, bool fHelp, const CPubKey return ret; } -static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew,uint8_t *opretbuf,int32_t opretlen,long int opretValue) +static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew,uint8_t *opretbuf,int32_t opretlen,long int opretValue, int64_t unlockTime = 0LL) { CAmount curBalance = pwalletMain->GetBalance(); @@ -481,8 +474,17 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr if (nValue > curBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); - // Parse Zcash address - CScript scriptPubKey = GetScriptForDestination(address); + // make scriptPubKey for destination + CScriptExt scriptPubKey; + if (unlockTime == 0) + scriptPubKey = GetScriptForDestination(address); + else { + CKeyID keyid; + if (CBitcoinAddress(address).GetKeyID(keyid)) + scriptPubKey.TimeLockSpend(keyid, unlockTime); + } + if (scriptPubKey.empty()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid destination"); // Create and send the transaction CReserveKey reservekey(pwalletMain); @@ -514,21 +516,19 @@ 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; + 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) throw runtime_error( - "sendtoaddress \"" + strprintf("%s",komodo_chainname()) + "_address\" amount ( \"comment\" \"comment-to\" subtractfeefromamount )\n" + "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" + HelpRequiringPassphrase() + "\nArguments:\n" - "1. \"" + strprintf("%s",komodo_chainname()) + "_address\" (string, required) The " + strprintf("%s",komodo_chainname()) + " address to send to.\n" + "1. \"" + strprintf("%s", komodo_chainname()) + "_address\" (string, required) The " + strprintf("%s",komodo_chainname()) + " address to send to.\n" "2. \"amount\" (numeric, required) The amount in " + strprintf("%s",komodo_chainname()) + " to send. eg 0.1\n" "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" " This is not part of the transaction, just kept in your wallet.\n" @@ -536,13 +536,15 @@ 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" + "6. oprethexstr\n" "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" "\nExamples:\n" + HelpExampleCli("sendtoaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 0.1") + HelpExampleCli("sendtoaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 0.1 \"donation\" \"seans outpost\"") + HelpExampleCli("sendtoaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 0.1 \"\" \"\" true") + // test cltv: + HelpExampleCli("sendtoaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 0.1 \"donation\" \"seans outpost\" false \"\" 1634663625") + HelpExampleRpc("sendtoaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", 0.1, \"donation\", \"seans outpost\"") ); @@ -575,47 +577,32 @@ 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 ) - { + 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); + decode_hex(opretbuf, len, oprethexstr); + opretlen = komodo_opreturnscript(opretscript, 0x00, opretbuf, len); opret = opretscript; - } else opretlen = 0; + } else + opretlen = 0; } + + /* test cltv coins: + int64_t unlockTime = 0LL; + if (params.size() > 6) { + unlockTime = atoll(params[6].get_str().c_str()); + if (unlockTime < 0LL) + throw JSONRPCError(RPC_TYPE_ERROR, "invalid unlock time"); + } */ + EnsureWalletIsUnlocked(); - SendMoney(dest, nAmount, fSubtractFeeFromAmount, wtx,opret,opretlen,0); + SendMoney(dest, nAmount, fSubtractFeeFromAmount, wtx, opret, opretlen, 0, 0LL); return wtx.GetHash().GetHex(); } -#include "komodo_defs.h" - -#define KOMODO_KVPROTECTED 1 -#define KOMODO_KVBINARY 2 -#define KOMODO_KVDURATION 1440 -#define IGUANA_MAXSCRIPTSIZE 10001 -uint64_t PAX_fiatdest(uint64_t *seedp,int32_t tokomodo,char *destaddr,uint8_t pubkey37[37],char *coinaddr,int32_t height,char *base,int64_t fiatoshis); -int32_t komodo_opreturnscript(uint8_t *script,uint8_t type,uint8_t *opret,int32_t opretlen); -#define CRYPTO777_KMDADDR "RXL3YXG2ceaB6C5hfJcN4fvmLH2C34knhA" -extern int32_t KOMODO_PAX; -extern uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE; -int32_t komodo_is_issuer(); -int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); -int32_t komodo_isrealtime(int32_t *kmdheightp); -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 komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen); -int32_t komodo_kvcmp(uint8_t *refvalue,uint16_t refvaluesize,uint8_t *value,uint16_t valuesize); -uint64_t komodo_kvfee(uint32_t flags,int32_t opretlen,int32_t keylen); -uint256 komodo_kvsig(uint8_t *buf,int32_t len,uint256 privkey); -int32_t komodo_kvduration(uint32_t flags); -uint256 komodo_kvprivkey(uint256 *pubkeyp,char *passphrase); -int32_t komodo_kvsigverify(uint8_t *buf,int32_t len,uint256 _pubkey,uint256 sig); - UniValue kvupdate(const UniValue& params, bool fHelp, const CPubKey& mypk) { static uint256 zeroes; @@ -869,14 +856,27 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp, const CPubKey& LOCK2(cs_main, pwalletMain->cs_wallet); + int64_t txLockTime = komodo_next_tx_locktime(); UniValue jsonGroupings(UniValue::VARR); - std::map balances = pwalletMain->GetAddressBalances(); - for (const std::set& grouping : pwalletMain->GetAddressGroupings()) { + std::map balances = pwalletMain->GetAddressBalances(txLockTime); + for (const std::set& grouping : pwalletMain->GetAddressGroupings(txLockTime)) { UniValue jsonGrouping(UniValue::VARR); for (const CTxDestination& address : grouping) { UniValue addressInfo(UniValue::VARR); addressInfo.push_back(EncodeDestination(address)); + if (address.which() == TX_CLTV) { + const CCLTVID &cltv = boost::get(address); + if (cltv.IsUnlocked()) + addressInfo.push_back("CLTV-spendable"); + else { + addressInfo.push_back("CLTV-locked"); + if (cltv.GetUnlockTime() > LOCKTIME_THRESHOLD) + addressInfo.push_back( strprintf("%lld (%s)", cltv.GetUnlockTime(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S UTC", cltv.GetUnlockTime())) ); + else + addressInfo.push_back( strprintf("%lld", cltv.GetUnlockTime()) ); + } + } addressInfo.push_back(ValueFromAmount(balances[address])); { if (pwalletMain->mapAddressBook.find(address) != pwalletMain->mapAddressBook.end()) { @@ -5513,10 +5513,6 @@ UniValue z_listoperationids(const UniValue& params, bool fHelp, const CPubKey& m } -#include "script/sign.h" -int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); -extern std::string NOTARY_PUBKEY; - int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33, void *pTr) { set setAddress; uint8_t *script,utxosig[128]; uint256 utxotxid; uint64_t utxovalue; int32_t i,siglen=0,nMinDepth = 0,nMaxDepth = 9999999; vector vecOutputs; uint32_t utxovout,eligible,earliest = 0; CScript best_scriptPubKey; bool fNegative,fOverflow; @@ -5609,7 +5605,7 @@ int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits } -int32_t ensure_CCrequirements(uint8_t evalcode) +int32_t ensure_CCrequirements(uint8_t evalcode, bool isRemote) { CCerror.clear(); if (ASSETCHAINS_CCDISABLES[evalcode] != 0) { @@ -5620,7 +5616,7 @@ int32_t ensure_CCrequirements(uint8_t evalcode) return (-1); } } - if (NOTARY_PUBKEY33[0] == 0) { + if (!isRemote && NOTARY_PUBKEY33[0] == 0) { // do not check local pubkey set for nspv calls fprintf(stderr, "no -pubkey set\n"); return (-1); } else if (GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX) == 0) { @@ -6728,144 +6724,148 @@ UniValue gatewaysprocessed(const UniValue& params, bool fHelp, const CPubKey& my UniValue oracleslist(const UniValue& params, bool fHelp, const CPubKey& mypk) { - if ( fHelp || params.size() > 0 ) + if (fHelp || params.size() > 0) throw runtime_error("oracleslist\n"); - if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) + if (ensure_CCrequirements(EVAL_ORACLES) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - return(OraclesList()); + return (OraclesList()); } UniValue oraclesinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 txid; - if ( fHelp || params.size() != 1 ) + if (fHelp || params.size() != 1) throw runtime_error("oraclesinfo oracletxid\n"); - if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) + if (ensure_CCrequirements(EVAL_ORACLES) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - txid = Parseuint256((char *)params[0].get_str().c_str()); - return(OracleInfo(txid)); + txid = Parseuint256((char*)params[0].get_str().c_str()); + return (OracleInfo(txid)); } UniValue oraclesfund(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue result(UniValue::VOBJ); uint256 txid; - if ( fHelp || params.size() != 1 ) + UniValue result(UniValue::VOBJ); + uint256 txid; + if (fHelp || params.size() != 1) throw runtime_error("oraclesfund oracletxid\n"); - if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) + if (ensure_CCrequirements(EVAL_ORACLES) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - Lock2NSPV(mypk); - txid = Parseuint256((char *)params[0].get_str().c_str()); - result = OracleFund(mypk,0,txid); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) - { + CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !mypk.IsValid()); // dont lock wallet for nspv calls + txid = Parseuint256((char*)params[0].get_str().c_str()); + result = OracleFund(mypk, 0, txid); + if (result[JSON_HEXTX].getValStr().size() > 0) { result.push_back(Pair("result", "success")); } - Unlock2NSPV(mypk); - return(result); + return (result); } UniValue oraclesregister(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue result(UniValue::VOBJ); uint256 txid; int64_t datafee; - if ( fHelp || params.size() != 2 ) - throw runtime_error("oraclesregister oracletxid datafee\n"); - if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) + UniValue result(UniValue::VOBJ); + uint256 txid; + if (fHelp || params.size() != 2) + throw runtime_error("oraclesregister oracletxid datafee\n" + " datafee in satoshis"); + if (ensure_CCrequirements(EVAL_ORACLES) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - Lock2NSPV(mypk); - txid = Parseuint256((char *)params[0].get_str().c_str()); - if ( (datafee= atol((char *)params[1].get_str().c_str())) == 0 ) - datafee = atof((char *)params[1].get_str().c_str()) * COIN + 0.00000000499999; - result = OracleRegister(mypk,0,txid,datafee); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) - { + CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !mypk.IsValid()); // dont lock wallet for nspv calls + txid = Parseuint256((char*)params[0].get_str().c_str()); + CAmount datafee = atoll((char *)params[1].get_str().c_str()); + result = OracleRegister(mypk, 0, txid, datafee); + if (result[JSON_HEXTX].getValStr().size() > 0) { result.push_back(Pair("result", "success")); } - Unlock2NSPV(mypk); - return(result); + return (result); } UniValue oraclessubscribe(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue result(UniValue::VOBJ); uint256 txid; int64_t amount; std::vector pubkey; - if ( fHelp || params.size() != 3 ) + UniValue result(UniValue::VOBJ); + uint256 txid; + std::vector pubkey; + + if (fHelp || params.size() != 3) throw runtime_error("oraclessubscribe oracletxid publisher amount\n"); - if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) + if (ensure_CCrequirements(EVAL_ORACLES) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - Lock2NSPV(mypk); - txid = Parseuint256((char *)params[0].get_str().c_str()); + CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !mypk.IsValid()); // dont lock wallet for nspv calls + txid = Parseuint256((char*)params[0].get_str().c_str()); pubkey = ParseHex(params[1].get_str().c_str()); - amount = atof((char *)params[2].get_str().c_str()) * COIN + 0.00000000499999; - result = OracleSubscribe(mypk,0,txid,pubkey2pk(pubkey),amount); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) - { + CAmount amount = AmountFromValue(params[2]); + result = OracleSubscribe(mypk, 0, txid, pubkey2pk(pubkey), amount); + if (result[JSON_HEXTX].getValStr().size() > 0) { result.push_back(Pair("result", "success")); } - Unlock2NSPV(mypk); - return(result); + return (result); } UniValue oraclessample(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue result(UniValue::VOBJ); uint256 oracletxid,txid; int32_t num; char *batonaddr; - if ( fHelp || params.size() != 2 ) + UniValue result(UniValue::VOBJ); + uint256 oracletxid, txid; + int32_t num; + char* batonaddr; + if (fHelp || params.size() != 2) throw runtime_error("oraclessample oracletxid txid\n"); - if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) + if (ensure_CCrequirements(EVAL_ORACLES) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - oracletxid = Parseuint256((char *)params[0].get_str().c_str()); - txid = Parseuint256((char *)params[1].get_str().c_str()); - return(OracleDataSample(oracletxid,txid)); + oracletxid = Parseuint256((char*)params[0].get_str().c_str()); + txid = Parseuint256((char*)params[1].get_str().c_str()); + return (OracleDataSample(oracletxid, txid)); } UniValue oraclessamples(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue result(UniValue::VOBJ); uint256 txid; int32_t num; char *batonaddr; - if ( fHelp || params.size() != 3 ) + UniValue result(UniValue::VOBJ); + uint256 txid; + int32_t num; + char* batonaddr; + if (fHelp || params.size() != 3) throw runtime_error("oraclessamples oracletxid batonaddress num\n"); - if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) + if (ensure_CCrequirements(EVAL_ORACLES) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - txid = Parseuint256((char *)params[0].get_str().c_str()); - batonaddr = (char *)params[1].get_str().c_str(); - num = atoi((char *)params[2].get_str().c_str()); - return(OracleDataSamples(txid,batonaddr,num)); + txid = Parseuint256((char*)params[0].get_str().c_str()); + batonaddr = (char*)params[1].get_str().c_str(); + num = atoi((char*)params[2].get_str().c_str()); + return (OracleDataSamples(txid, batonaddr, num)); } UniValue oraclesdata(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue result(UniValue::VOBJ); uint256 txid; std::vector data; - if ( fHelp || params.size() != 2 ) + UniValue result(UniValue::VOBJ); + uint256 txid; + std::vector data; + if (fHelp || params.size() != 2) throw runtime_error("oraclesdata oracletxid hexstr\n"); - if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) + if (ensure_CCrequirements(EVAL_ORACLES) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - Lock2NSPV(mypk); - txid = Parseuint256((char *)params[0].get_str().c_str()); + CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !mypk.IsValid()); // dont lock wallet for nspv calls + txid = Parseuint256((char*)params[0].get_str().c_str()); data = ParseHex(params[1].get_str().c_str()); - result = OracleData(mypk,0,txid,data); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) - { + result = OracleData(mypk, 0, txid, data); + if (result[JSON_HEXTX].getValStr().size() > 0) { result.push_back(Pair("result", "success")); } - Unlock2NSPV(mypk); - return(result); + return (result); } UniValue oraclescreate(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue result(UniValue::VOBJ); std::string name,description,format; - if ( fHelp || params.size() != 3 ) + UniValue result(UniValue::VOBJ); + std::string name, description, format; + if (fHelp || params.size() != 3) throw runtime_error("oraclescreate name description format\n"); - if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) + if (ensure_CCrequirements(EVAL_ORACLES) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - Lock2NSPV(mypk); + CONDITIONAL_LOCK2(cs_main, pwalletMain->cs_wallet, !mypk.IsValid()); // dont lock wallet for nspv calls name = params[0].get_str(); description = params[1].get_str(); format = params[2].get_str(); - result = OracleCreate(mypk,0,name,description,format); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) - { + result = OracleCreate(mypk, 0, name, description, format); + if (result[JSON_HEXTX].getValStr().size() > 0) { result.push_back(Pair("result", "success")); } - Unlock2NSPV(mypk); - return(result); + return (result); } UniValue FSMcreate(const UniValue& params, bool fHelp, const CPubKey& mypk) diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index 3e7a54d4afe..226647b1986 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -24,5 +24,6 @@ class CRPCTable; void RegisterWalletRPCCommands(CRPCTable &tableRPC); bool EnsureWalletIsAvailable(bool avoidException); +void EnsureWalletIsUnlocked(); #endif //BITCOIN_WALLET_RPCWALLET_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index db66de052c4..461657af93e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -38,6 +38,8 @@ #include "crypter.h" #include "coins.h" #include "zcash/zip32.h" + +#include "komodo_defs.h" #include "cc/CCinclude.h" #include @@ -58,12 +60,7 @@ unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; bool bSpendZeroConfChange = true; bool fSendFreeTransactions = false; bool fPayAtLeastCustomFee = true; -#include "komodo_defs.h" -CBlockIndex *komodo_chainactive(int32_t height); -extern std::string DONATION_PUBKEY; -int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); -int tx_height( const uint256 &hash ); /** * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) @@ -2215,8 +2212,9 @@ isminetype CWallet::IsMine(const CTransaction& tx, uint32_t voutNum) vector vSolutions; txnouttype whichType; const CScriptExt scriptPubKey = CScriptExt(tx.vout[voutNum].scriptPubKey); + bool iscltv; - if (!Solver(scriptPubKey, whichType, vSolutions)) { + if (!SolverCLTV(scriptPubKey, whichType, vSolutions, iscltv)) { if (this->HaveWatchOnly(scriptPubKey)) return ISMINE_WATCH_ONLY; return ISMINE_NO; @@ -2233,6 +2231,8 @@ isminetype CWallet::IsMine(const CTransaction& tx, uint32_t voutNum) case TX_NULL_DATA: break; + // Note: this is unusable in komodo + // Unlike in Verus the wallet in Komodo can't understand cryptoconditions spks (only appropriate cc modules understand their spks) case TX_CRYPTOCONDITION: // for now, default is that the first value returned will be the script, subsequent values will be // pubkeys. if we have the first pub key in our wallet, we consider this spendable @@ -2260,7 +2260,7 @@ isminetype CWallet::IsMine(const CTransaction& tx, uint32_t voutNum) scriptID = CScriptID(uint160(vSolutions[0])); if (this->GetCScript(scriptID, subscript)) { - // if this is a CLTV, handle it differently + // if this is a P2SH/CLTV, handle it differently if (subscript.IsCheckLockTimeVerify()) { return (::IsMine(*this, subscript)); @@ -2276,6 +2276,7 @@ isminetype CWallet::IsMine(const CTransaction& tx, uint32_t voutNum) tx.vout[voutNext].scriptPubKey.size() > 7 && tx.vout[voutNext].scriptPubKey[0] == OP_RETURN) { + // Verus specific: // get the opret script from next vout, verify that the front is CLTV and hash matches // if so, remove it and use the solver opcodetype op; @@ -3385,10 +3386,7 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const /** * populate vCoins with vector of available COutputs. */ -uint64_t komodo_interestnew(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); -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); - -void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue, bool fIncludeCoinBase) const +void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue, bool fIncludeCoinBase, int64_t txLockTime) const { uint64_t interest,*ptr; vCoins.clear(); @@ -3464,7 +3462,15 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const (*ptr) = 0; } } - vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO)); + + bool bStillTimeLocked = false; + { + int64_t nLockTime; + if(pcoin->vout[i].scriptPubKey.IsCheckLockTimeVerify(&nLockTime)) + bStillTimeLocked = !TokelCheckLockTimeHelper(nLockTime, txLockTime); + } + + vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO && !bStillTimeLocked)); } } } @@ -3639,7 +3645,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int return true; } -bool CWallet::SelectCoins(const CAmount& nTargetValue, set >& setCoinsRet, CAmount& nValueRet, bool& fOnlyCoinbaseCoinsRet, bool& fNeedCoinbaseCoinsRet, const CCoinControl* coinControl) const +bool CWallet::SelectCoins(const CAmount& nTargetValue, set >& setCoinsRet, CAmount& nValueRet, bool& fOnlyCoinbaseCoinsRet, bool& fNeedCoinbaseCoinsRet, const CCoinControl* coinControl, int64_t txLockTime) const { // Output parameter fOnlyCoinbaseCoinsRet is set to true when the only available coins are coinbase utxos. uint64_t tmp; int32_t retval; @@ -3649,8 +3655,8 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set vCoinsNoCoinbase, vCoinsWithCoinbase; - AvailableCoins(vCoinsNoCoinbase, true, coinControl, false, false); - AvailableCoins(vCoinsWithCoinbase, true, coinControl, false, true); + AvailableCoins(vCoinsNoCoinbase, true, coinControl, false, false, txLockTime); + AvailableCoins(vCoinsWithCoinbase, true, coinControl, false, true, txLockTime); fOnlyCoinbaseCoinsRet = vCoinsNoCoinbase.size() == 0 && vCoinsWithCoinbase.size() > 0; // If coinbase utxos can only be sent to zaddrs, exclude any coinbase utxos from coin selection. @@ -3727,6 +3733,7 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set& vecSend, CWalletTx& wt CMutableTransaction txNew = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextBlockHeight); //if ((uint32_t)chainActive.LastTip()->nTime < ASSETCHAINS_STAKED_HF_TIMESTAMP) - if ( !komodo_hardfork_active((uint32_t)chainActive.LastTip()->nTime) ) + /*if ( !komodo_hardfork_active((uint32_t)chainActive.LastTip()->nTime) ) txNew.nLockTime = (uint32_t)chainActive.LastTip()->nTime + 1; // set to a time close to now else - txNew.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast(); + txNew.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast();*/ + txNew.nLockTime = komodo_next_tx_locktime(); // Activates after Overwinter network upgrade if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { @@ -3910,7 +3918,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt bool fOnlyCoinbaseCoins = false; bool fNeedCoinbaseCoins = false; interest2 = 0; - if (!SelectCoins(nTotalValue, setCoins, nValueIn, fOnlyCoinbaseCoins, fNeedCoinbaseCoins, coinControl)) + if (!SelectCoins(nTotalValue, setCoins, nValueIn, fOnlyCoinbaseCoins, fNeedCoinbaseCoins, coinControl, txNew.nLockTime)) { if (fOnlyCoinbaseCoins && Params().GetConsensus().fCoinbaseMustBeProtected) { strFailReason = _("Coinbase funds can only be sent to a zaddr"); @@ -4187,7 +4195,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) // Broadcast if (!wtxNew.AcceptToMemoryPool(false)) { - fprintf(stderr,"commit failed\n"); + // fprintf(stderr,"commit failed\n"); // This must not fail. The transaction has already been signed and recorded. LogPrintf("CommitTransaction(): Error: Transaction not valid\n"); return false; @@ -4222,7 +4230,7 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge } -void komodo_prefetch(FILE *fp); +//void komodo_prefetch(FILE *fp); DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { @@ -4481,7 +4489,7 @@ int64_t CWallet::GetOldestKeyPoolTime() return keypool.nTime; } -std::map CWallet::GetAddressBalances() +std::map CWallet::GetAddressBalances(int64_t txLockTime) { map balances; @@ -4508,6 +4516,9 @@ std::map CWallet::GetAddressBalances() continue; if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr)) continue; + // if this is CLTV then set if it is already unlocked for spending for the next txLockTime: + if (addr.which() == TX_CLTV) + TokelSetIfTimeUnlocked(addr, txLockTime); CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue; @@ -4521,7 +4532,7 @@ std::map CWallet::GetAddressBalances() return balances; } -set< set > CWallet::GetAddressGroupings() +set< set > CWallet::GetAddressGroupings(int64_t txLockTime) { AssertLockHeld(cs_wallet); // mapWallet set< set > groupings; @@ -4542,6 +4553,8 @@ set< set > CWallet::GetAddressGroupings() continue; if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address)) continue; + if (address.which() == TX_CLTV) + TokelSetIfTimeUnlocked(address, txLockTime); grouping.insert(address); any_mine = true; } @@ -4549,14 +4562,16 @@ set< set > CWallet::GetAddressGroupings() // group change with input addresses if (any_mine) { - BOOST_FOREACH(CTxOut txout, pcoin->vout) - if (IsChange(txout)) - { - CTxDestination txoutAddr; - if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) - continue; - grouping.insert(txoutAddr); - } + BOOST_FOREACH (CTxOut txout, pcoin->vout) + if (IsChange(txout)) { + CTxDestination txoutAddr; + if (!ExtractDestination(txout.scriptPubKey, txoutAddr)) + continue; + // if this is CLTV then set if it is already unlocked for spending for the next txLockTime: + if (txoutAddr.which() == TX_CLTV) + TokelSetIfTimeUnlocked(txoutAddr, txLockTime); + grouping.insert(txoutAddr); + } } if (grouping.size() > 0) { @@ -4572,6 +4587,11 @@ set< set > CWallet::GetAddressGroupings() CTxDestination address; if(!ExtractDestination(pcoin->vout[i].scriptPubKey, address)) continue; + + // if this is CLTV then set if it is already unlocked for spending for the next txLockTime: + if (address.which() == TX_CLTV) + TokelSetIfTimeUnlocked(address, txLockTime); + grouping.insert(address); groupings.insert(grouping); grouping.clear(); @@ -4831,6 +4851,14 @@ class CAffectedKeysVisitor : public boost::static_visitor { Process(script); } + void operator()(const CCLTVID &cltv) { + CKeyID keyId = cltv.GetID(); + if (keystore.HaveKey(keyId)) + vKeys.push_back(keyId); + } + + void operator()(const CCryptoConditionID &none) {} // no cc in the wallet + void operator()(const CNoDestination &none) {} }; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 718376429fe..a8056acd4dc 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -748,7 +748,7 @@ class CAccountingEntry class CWallet : public CCryptoKeyStore, public CValidationInterface { private: - bool SelectCoins(const CAmount& nTargetValue, std::set >& setCoinsRet, CAmount& nValueRet, bool& fOnlyCoinbaseCoinsRet, bool& fNeedCoinbaseCoinsRet, const CCoinControl *coinControl = NULL) const; + bool SelectCoins(const CAmount& nTargetValue, std::set >& setCoinsRet, CAmount& nValueRet, bool& fOnlyCoinbaseCoinsRet, bool& fNeedCoinbaseCoinsRet, const CCoinControl *coinControl = NULL, int64_t txLockTime = 0LL) const; CWalletDB *pwalletdbEncryption; @@ -993,7 +993,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! check whether we are allowed to upgrade (or already support) to the named feature bool CanSupportFeature(enum WalletFeature wf) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } - void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false, bool fIncludeCoinBase=true) const; + void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false, bool fIncludeCoinBase=true, int64_t txLockTime = 0L) const; bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, CAmount& nValueRet) const; bool IsSpent(const uint256& hash, unsigned int n) const; @@ -1171,8 +1171,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface int64_t GetOldestKeyPoolTime(); void GetAllReserveKeys(std::set& setAddress) const; - std::set< std::set > GetAddressGroupings(); - std::map GetAddressBalances(); + std::set< std::set > GetAddressGroupings(int64_t txUnlockTime); + std::map GetAddressBalances(int64_t txUnlockTime); std::set GetAccountAddresses(const std::string& strAccount) const; @@ -1518,4 +1518,17 @@ class AddSpendingKeyToWallet : public boost::static_visitor &vCoins, int64_t txLockTime) +{ + // remove still timelocked coins + for (std::vector::iterator it = vCoins.begin(); it != vCoins.end();) + { + int64_t nLockTime; + if (it->tx->vout[it->i].scriptPubKey.IsCheckLockTimeVerify(&nLockTime) && !TokelCheckLockTimeHelper(nLockTime, txLockTime)) + it = vCoins.erase(it); + else + ++it; + } +}*/ + #endif // BITCOIN_WALLET_WALLET_H diff --git a/src/wallet/wallet_ismine.cpp b/src/wallet/wallet_ismine.cpp index 55b5e31defd..04df2e21996 100644 --- a/src/wallet/wallet_ismine.cpp +++ b/src/wallet/wallet_ismine.cpp @@ -26,6 +26,9 @@ #include "script/standard.h" #include "cc/eval.h" +#include "chain.h" +extern CChain chainActive; + #include using namespace std; @@ -68,17 +71,9 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& _scriptPubKey, vector vSolutions; txnouttype whichType; CScript scriptPubKey = _scriptPubKey; - - if (scriptPubKey.IsCheckLockTimeVerify()) - { - uint8_t pushOp = scriptPubKey[0]; - uint32_t scriptStart = pushOp + 3; - - // continue with post CLTV script - scriptPubKey = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end()); - } + bool iscltv; - if (!Solver(scriptPubKey, whichType, vSolutions)) { + if (!SolverCLTV(scriptPubKey, whichType, vSolutions, iscltv)) { if (keystore.HaveWatchOnly(scriptPubKey)) return ISMINE_WATCH_ONLY; return ISMINE_NO; diff --git a/zcutil/build-mac-dtest.sh b/zcutil/build-mac-dtest.sh index 8104c720009..e1a5ece470f 100755 --- a/zcutil/build-mac-dtest.sh +++ b/zcutil/build-mac-dtest.sh @@ -18,11 +18,13 @@ Usage: $0 --help Show this help message and exit. -$0 [ --enable-lcov ] [ MAKEARGS... ] - Build Zcash and most of its transitive dependencies from - source. MAKEARGS are applied to both dependencies and Zcash itself. If - --enable-lcov is passed, Zcash is configured to add coverage +$0 [ --enable-lcov ] [ --enable-debug ] [ MAKEARGS... ] + Build Komodo and most of its transitive dependencies from + source. MAKEARGS are applied to both dependencies and Komodo itself. + If --enable-lcov is passed, Komodo is configured to add coverage instrumentation, thus enabling "make cov" to work. + If --enable-debug is passed, Komodo is built with debugging information. It + must be passed after the previous arguments, if present. EOF exit 0 fi @@ -37,22 +39,25 @@ then shift fi +# If --enable-debug is the next argument, enable debugging +DEBUGGING_ARG='' +if [ "x${1:-}" = 'x--enable-debug' ] +then + DEBUG=1 + export DEBUG + DEBUGGING_ARG='--enable-debug' + shift +fi + TRIPLET=`./depends/config.guess` PREFIX="$(pwd)/depends/$TRIPLET" +# make dependences make "$@" -C ./depends/ V=1 NO_QT=1 NO_PROTON=1 -#BUILD CCLIB - -WD=$PWD -cd src/cc -echo $PWD -./makecustom -cd $WD - ./autogen.sh CPPFLAGS="-I$PREFIX/include -arch x86_64 -DTESTMODE" LDFLAGS="-L$PREFIX/lib -arch x86_64 -Wl,-no_pie" \ -CXXFLAGS='-arch x86_64 -I/usr/local/Cellar/gcc\@8/8.3.0/include/c++/8.3.0/ -I$PREFIX/include -fwrapv -fno-strict-aliasing -Wno-builtin-declaration-mismatch -Werror -Wno-error=attributes -g -Wl,-undefined -Wl,dynamic_lookup' \ -./configure --prefix="${PREFIX}" --with-gui=no "$HARDENING_ARG" "$LCOV_ARG" +CXXFLAGS="-arch x86_64 -I/usr/local/Cellar/gcc\@8/8.3.0/include/c++/8.3.0/ -I$PREFIX/include -fwrapv -fno-strict-aliasing -Wno-builtin-declaration-mismatch -Werror -Wno-error=attributes -g -Wl,-undefined -Wl,dynamic_lookup" \ +./configure --prefix="${PREFIX}" --with-gui=no "$HARDENING_ARG" "$LCOV_ARG" "$DEBUGGING_ARG" make "$@" V=1 NO_GTEST=1 STATIC=1 diff --git a/zcutil/build-mac.sh b/zcutil/build-mac.sh index dd5b578e50a..c4e0cb518df 100755 --- a/zcutil/build-mac.sh +++ b/zcutil/build-mac.sh @@ -23,11 +23,14 @@ Usage: $0 --help Show this help message and exit. -$0 [ --enable-lcov ] [ MAKEARGS... ] - Build Zcash and most of its transitive dependencies from - source. MAKEARGS are applied to both dependencies and Zcash itself. If - --enable-lcov is passed, Zcash is configured to add coverage +$0 [ --enable-lcov ] [ --enable-websockets ] [ --enable-debug ] [ MAKEARGS... ] + Build Komodo and most of its transitive dependencies from + source. MAKEARGS are applied to both dependencies and Komodo itself. + If --enable-lcov is passed, Komodo is configured to add coverage instrumentation, thus enabling "make cov" to work. + If --enable-websockets is passed then websockets support is added for nSPV protocol (disabled for now) + If --enable-debug is passed, Komodo is built with debugging information. It + must be passed after the previous arguments, if present. EOF exit 0 fi @@ -42,29 +45,35 @@ then shift fi +# If --enable-websockets is the next argument, enable websockets support for nspv clients: +WEBSOCKETS_ARG='' +# if [ "x${1:-}" = 'x--enable-websockets' ] +# then +# WEBSOCKETS_ARG='--enable-websockets=yes' +# shift +#fi + +# If --enable-debug is the next argument, enable debugging +DEBUGGING_ARG='' +if [ "x${1:-}" = 'x--enable-debug' ] +then + DEBUG=1 + export DEBUG + DEBUGGING_ARG='--enable-debug' + shift +fi + TRIPLET=`./depends/config.guess` PREFIX="$(pwd)/depends/$TRIPLET" make "$@" -C ./depends/ V=1 NO_QT=1 NO_PROTON=1 -#BUILD CCLIB - -WD=$PWD - -cd src/cc -echo $PWD -echo Making cclib... -./makecustom - -cd ./priceslibs -echo Making prices feeds custom libs... -make all - -cd $WD - ./autogen.sh CPPFLAGS="-I$PREFIX/include -arch x86_64" LDFLAGS="-L$PREFIX/lib -arch x86_64 -Wl,-no_pie" \ -CXXFLAGS='-arch x86_64 -I/usr/local/Cellar/gcc\@8/8.3.0/include/c++/8.3.0/ -I$PREFIX/include -fwrapv -fno-strict-aliasing -Wno-builtin-declaration-mismatch -Werror -Wno-error=attributes -g -Wl,-undefined -Wl,dynamic_lookup' \ -./configure --prefix="${PREFIX}" --with-gui=no "$HARDENING_ARG" "$LCOV_ARG" "$CONFIGURE_FLAGS" +CXXFLAGS="-arch x86_64 -I/usr/local/Cellar/gcc\@8/8.3.0/include/c++/8.3.0/ -fwrapv -fno-strict-aliasing -Wno-builtin-declaration-mismatch -Werror -Wno-error=attributes -g -Wl,-undefined -Wl,dynamic_lookup" \ +./configure --prefix="${PREFIX}" --with-gui=no "$HARDENING_ARG" "$LCOV_ARG" "$CONFIGURE_FLAGS" "$WEBSOCKETS_ARG" "$DEBUGGING_ARG" \ + --with-custom-bin=yes CUSTOM_BIN_NAME=tokel CUSTOM_BRAND_NAME=Tokel \ + CUSTOM_SERVER_ARGS="'-ac_name=TOKEL -ac_supply=100000000 -ac_eras=2 -ac_cbmaturity=1 -ac_reward=100000000,4250000000 -ac_end=80640,0 -ac_decay=0,77700000 -ac_halving=0,525600 -ac_cc=555 -ac_ccenable=236,245,246,247 -ac_adaptivepow=6 -addnode=135.125.204.169 -addnode=192.99.71.125 -nspv_msg=1'" \ + CUSTOM_CLIENT_ARGS='-ac_name=TOKEL' -make "$@" V=1 NO_GTEST=1 STATIC=1 +make "$@" V=1 NO_GTEST=1 STATIC=1 \ No newline at end of file diff --git a/zcutil/build-win-dtest.sh b/zcutil/build-win-dtest.sh index 00a64de5bf7..48557bc5e58 100755 --- a/zcutil/build-win-dtest.sh +++ b/zcutil/build-win-dtest.sh @@ -2,23 +2,24 @@ export HOST=x86_64-w64-mingw32 CXX=x86_64-w64-mingw32-g++-posix CC=x86_64-w64-mingw32-gcc-posix -PREFIX="$(pwd)/depends/$HOST" set -eu -o pipefail - set -x -cd "$(dirname "$(readlink -f "$0")")/.." -cd depends/ && make HOST=$HOST V=1 NO_QT=1 -cd ../ -WD=$PWD -cd src/cc -echo $PWD -./makecustom -cd $WD +UTIL_DIR="$(dirname "$(readlink -f "$0")")" +BASE_DIR="$(dirname "$(readlink -f "$UTIL_DIR")")" +PREFIX="$BASE_DIR/depends/$HOST" + +cd $BASE_DIR/depends +make HOST=$HOST NO_QT=1 "$@" +cd $BASE_DIR ./autogen.sh -CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site CXXFLAGS="-DPTW32_STATIC_LIB -DCURL_STATICLIB -DCURVE_ALT_BN128 -fopenmp -pthread" CPPFLAGS=-DTESTMODE ./configure --prefix="${PREFIX}" --host=x86_64-w64-mingw32 --enable-static --disable-shared +CONFIG_SITE=$PWD/depends/$HOST/share/config.site CXXFLAGS="-DPTW32_STATIC_LIB -DCURL_STATICLIB -DCURVE_ALT_BN128 -fopenmp -pthread" CPPFLAGS=-DTESTMODE ./configure --prefix="${PREFIX}" --host=$HOST --enable-static --disable-shared sed -i 's/-lboost_system-mt /-lboost_system-mt-s /' configure -cd src/ -CC="${CC} -g " CXX="${CXX} -g " make V=1 komodod.exe komodo-cli.exe komodo-tx.exe + +cd $BASE_DIR/src/cryptoconditions +CC="${CC} -g " CXX="${CXX} -g " make V=1 +cd $BASE_DIR + +CC="${CC} -g " CXX="${CXX} -g " make V=1 src/komodod.exe src/komodo-cli.exe src/komodo-tx.exe diff --git a/zcutil/build-win.sh b/zcutil/build-win.sh index e8c0465d98b..e1546d18bae 100755 --- a/zcutil/build-win.sh +++ b/zcutil/build-win.sh @@ -6,19 +6,34 @@ PREFIX="$(pwd)/depends/$HOST" set -eu -o pipefail -set -x -cd "$(dirname "$(readlink -f "$0")")/.." +UTIL_DIR="$(dirname "$(readlink -f "$0")")" +BASE_DIR="$(dirname "$(readlink -f "$UTIL_DIR")")" +PREFIX="$BASE_DIR/depends/$HOST" +# disable for code audit +# If --enable-websockets is the next argument, enable websockets support for nspv clients: +WEBSOCKETS_ARG='' +# if [ "x${1:-}" = 'x--enable-websockets' ] +# then +# WEBSOCKETS_ARG='--enable-websockets=yes' +# shift +# fi + +# make dependences cd depends/ && make HOST=$HOST V=1 NO_QT=1 cd ../ -WD=$PWD -cd src/cc -echo $PWD -./makecustom -cd $WD + +cd $BASE_DIR/depends +make HOST=$HOST NO_QT=1 "$@" +cd $BASE_DIR ./autogen.sh -CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site CXXFLAGS="-DPTW32_STATIC_LIB -DCURL_STATICLIB -DCURVE_ALT_BN128 -fopenmp -pthread" ./configure --prefix="${PREFIX}" --host=x86_64-w64-mingw32 --enable-static --disable-shared -sed -i 's/-lboost_system-mt /-lboost_system-mt-s /' configure +CONFIG_SITE=$BASE_DIR/depends/$HOST/share/config.site CXXFLAGS="-DPTW32_STATIC_LIB -DCURL_STATICLIB -DCURVE_ALT_BN128 -fopenmp -pthread" ./configure --prefix=$PREFIX --host=$HOST --enable-static --disable-shared "$WEBSOCKETS_ARG" \ + --with-custom-bin=yes CUSTOM_BIN_NAME=tokel CUSTOM_BRAND_NAME=Tokel \ + CUSTOM_SERVER_ARGS="'-ac_name=TOKEL -ac_supply=100000000 -ac_eras=2 -ac_cbmaturity=1 -ac_reward=100000000,4250000000 -ac_end=80640,0 -ac_decay=0,77700000 -ac_halving=0,525600 -ac_cc=555 -ac_ccenable=236,245,246,247 -ac_adaptivepow=6 -addnode=135.125.204.169 -addnode=192.99.71.125 -nspv_msg=1'" \ + CUSTOM_CLIENT_ARGS='-ac_name=TOKEL' +sed -i 's/-lboost_system-mt /-lboost_system-mt-s /' configure + cd src/ -CC="${CC} -g " CXX="${CXX} -g " make V=1 komodod.exe komodo-cli.exe komodo-tx.exe +# note: to build tokeld, tokel-cli it should not exist 'komodod.exe komodo-cli.exe' param here: +CC="${CC} -g " CXX="${CXX} -g " make V=1 diff --git a/zcutil/build.sh b/zcutil/build.sh index 6f625b185aa..8b40d0bab8f 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -44,17 +44,20 @@ then Usage: $0 --help Show this help message and exit. -$0 [ --enable-lcov || --disable-tests ] [ --disable-mining ] [ --enable-proton ] [ --disable-libs ] [ MAKEARGS... ] - Build Zcash and most of its transitive dependencies from - source. MAKEARGS are applied to both dependencies and Zcash itself. - If --enable-lcov is passed, Zcash is configured to add coverage +$0 [ --enable-lcov || --disable-tests ] [ --disable-mining ] [ --enable-proton ] [ --disable-libs ] [--enable-websockets] [ --enable-debug ] [ MAKEARGS... ] + Build Komodo and most of its transitive dependencies from + source. MAKEARGS are applied to both dependencies and Komodo itself. + If --enable-lcov is passed, Komodo is configured to add coverage instrumentation, thus enabling "make cov" to work. - If --disable-tests is passed instead, the Zcash tests are not built. - If --disable-mining is passed, Zcash is configured to not build any mining + If --disable-tests is passed instead, the Komodo tests are not built. + If --disable-mining is passed, Komodo is configured to not build any mining code. It must be passed after the test arguments, if present. - If --enable-proton is passed, Zcash is configured to build the Apache Qpid Proton + If --enable-proton is passed, Komodo is configured to build the Apache Qpid Proton library required for AMQP support. This library is not built by default. It must be passed after the test/mining arguments, if present. + If --enable-websockets is passed, Komodo is configured to build with websockets support for nspv protocol (disabled for now) + If --enable-debug is passed, Komodo is built with debugging information. It + must be passed after the previous arguments, if present. EOF exit 0 fi @@ -92,6 +95,30 @@ then shift fi +# If --enable-debug is the next argument, enable debugging +DEBUGGING_ARG='' +if [ "x${1:-}" = 'x--enable-debug' ] +then + DEBUG=1 + export DEBUG + DEBUGGING_ARG='--enable-debug' + shift +fi + +if [[ -z "${VERBOSE-}" ]]; then + VERBOSITY="--enable-silent-rules" +else + VERBOSITY="--disable-silent-rules" +fi + +# If --enable-websockets is the next argument, enable websockets support for nspv clients: +WEBSOCKETS_ARG='' +# if [ "x${1:-}" = 'x--enable-websockets' ] +# then +# WEBSOCKETS_ARG='--enable-websockets=yes' +# shift +# fi + eval "$MAKE" --version as --version ld -v @@ -99,17 +126,9 @@ ld -v HOST="$HOST" BUILD="$BUILD" NO_PROTON="$PROTON_ARG" "$MAKE" "$@" -C ./depends/ V=1 ./autogen.sh -CONFIG_SITE="$PWD/depends/$HOST/share/config.site" ./configure "$HARDENING_ARG" "$LCOV_ARG" "$TEST_ARG" "$MINING_ARG" "$PROTON_ARG" "$CONFIGURE_FLAGS" CXXFLAGS='-g' - -#BUILD CCLIB - -WD=$PWD - -cd src/cc -echo $PWD -./makecustom - - -cd $WD +CONFIG_SITE="$PWD/depends/$HOST/share/config.site" ./configure "$HARDENING_ARG" "$LCOV_ARG" "$TEST_ARG" "$MINING_ARG" "$PROTON_ARG" "$DEBUGGING_ARG" "$CONFIGURE_FLAGS" "$WEBSOCKETS_ARG" CXXFLAGS='-g' \ + --with-custom-bin=yes CUSTOM_BIN_NAME=tokel CUSTOM_BRAND_NAME=Tokel \ + CUSTOM_SERVER_ARGS="'-ac_name=TOKEL -ac_supply=100000000 -ac_eras=2 -ac_cbmaturity=1 -ac_reward=100000000,4250000000 -ac_end=80640,0 -ac_decay=0,77700000 -ac_halving=0,525600 -ac_cc=555 -ac_ccenable=236,245,246,247 -ac_adaptivepow=6 -addnode=135.125.204.169 -addnode=192.99.71.125 -nspv_msg=1'" \ + CUSTOM_CLIENT_ARGS='-ac_name=TOKEL' "$MAKE" "$@" V=1