diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b26399a9c..3ace05cb9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,112 +1,90 @@ stages: - build - test -- deploy ######################################################################################################################## ####START#### PROJECT LEVEL VARIABLES ####START#### ######################################################################################################################## variables: - VERSION: 0.4.0c - VERUS_CLI_LINUX: Verus-CLI-Linux-v${VERSION}.tar.gz - VERUS_CLI_WINDOWS: Verus-CLI-Windows-v${VERSION}.zip - VERUS_CLI_MACOS: Verus-CLI-MacOS-v${VERSION}.tar.gz - DOWNSTREAM_AGAMA_BRANCH: ${CI_COMMIT_REF_NAME} - POST_MESSAGE: "Source: ${CI_PROJECT_NAME}/${CI_COMMIT_REF_NAME}\n - Pipeline Trigger: ${CI_PIPELINE_SOURCE}\n - Commit: ${CI_COMMIT_SHA}$\n - ${CI_COMMIT_MESSAGE}" + PACKAGE_DIR_LINUX: linux64 + PACKAGE_DIR_WINDOWS: win64 + PACKAGE_DIR_MACOS: macos + AGAMA_ARTIFACTS_LINUX: ${PACKAGE_DIR_LINUX}.tar.gz + AGAMA_ARTIFACTS_WINDOWS: ${PACKAGE_DIR_WINDOWS}.zip + AGAMA_ARTIFACTS_MACOS: ${PACKAGE_DIR_MACOS}.tar.gz + AGAMA_ARTIFACTS_LINUX_CHECKSUM: ${AGAMA_ARTIFACTS_LINUX}.md5 + AGAMA_ARTIFACTS_WINDOWS_CHECKSUM: ${AGAMA_ARTIFACTS_WINDOWS}.md5 + AGAMA_ARTIFACTS_MACOS_CHECKSUM: ${AGAMA_ARTIFACTS_MACOS}.md5 + ARTIFACT_EXPIRATION: 1 week + ######################################################################################################################## ####END#### PROJECT LEVEL VARIABLES ####END#### ######################################################################################################################## ######################################################################################################################## ######################################################################################################################## -####START#### Build Stage: compile and package komodo binaries for Verus CLI ####START##### +####START#### Build Stage: compile and package komodo binaries for the Agama Wallet. ####START##### ######################################################################################################################## ######################################################################################################################## ####START#### LINUX ####START#### ######################################################################################################################## -build:linux: - image: asherd/verus-builders:verus-ubuntu +build:ubuntu: + stage: build + image: ca333/komodo-builders:komodo-ubuntu variables: DOCKER_DRIVER: overlay2 - stage: build cache: key: "${CI_JOB_NAME}${CI_COMMIT_REF_NAME}" paths: - depends/built script: - zcutil/build.sh -j$(nproc) - - mkdir verus-cli + - mkdir ${PACKAGE_DIR_LINUX} - cp src/komodod - src/komodo-cli - src/fiat/verus - src/verusd - doc/man/verus-cli/linux/README.txt - zcutil/fetch-params.sh - verus-cli - - mv verus-cli/fetch-params.sh verus-cli/fetch-params - - chmod +x verus-cli/komodod - - chmod +x verus-cli/komodo-cli - - chmod +x verus-cli/verus - - chmod +x verus-cli/verusd - - chmod +x verus-cli/fetch-params - - if [ "${CI_COMMIT_REF_NAME}" = "master" ]; then strip -g verus-cli/komodod && strip -g verus-cli/komodod; fi - - tar -czvf ${VERUS_CLI_LINUX} verus-cli - - md5sum ${VERUS_CLI_LINUX} > ${VERUS_CLI_LINUX}.md5 - - curl -F file=@"${VERUS_CLI_LINUX}" - -F channels="${CLI_POST_CHANNEL}" - -F initial_comment="${POST_MESSAGE}" - -H "${SLACK_BOT_AUTH}" - "https://slack.com/api/files.upload" + src/komodo-cli + ${PACKAGE_DIR_LINUX} + - chmod +x ${PACKAGE_DIR_LINUX}/komodod + - chmod +x ${PACKAGE_DIR_LINUX}/komodo-cli + - tar -czvf ${AGAMA_ARTIFACTS_LINUX} ${PACKAGE_DIR_LINUX} + - md5sum ${AGAMA_ARTIFACTS_LINUX} > ${AGAMA_ARTIFACTS_LINUX_CHECKSUM} artifacts: paths: - - ${VERUS_CLI_LINUX} - - ${VERUS_CLI_LINUX}.md5 + - ${AGAMA_ARTIFACTS_LINUX} + - ${AGAMA_ARTIFACTS_LINUX_CHECKSUM} expire_in: 1 week + ######################################################################################################################## ####END#### LINUX ####END#### ######################################################################################################################## ####START#### WINDOWS ####START#### ######################################################################################################################## build:windows: - image: asherd/verus-builders:verus-windows + stage: build + image: ca333/komodo-builders:komodo-windows variables: DOCKER_DRIVER: overlay2 - stage: build cache: key: "${CI_JOB_NAME}${CI_COMMIT_REF_NAME}" paths: - depends/built script: - zcutil/build-win.sh -j$(nproc) - - mkdir verus-cli + - mkdir ${PACKAGE_DIR_WINDOWS} - cp src/komodod.exe - src/komodo-cli.exe - src/komodo-tx.exe - src/fiat/verus.bat - src/verusd.bat - doc/man/verus-cli/windows/README.txt - zcutil/fetch-params.bat - zcutil/wget64.exe - verus-cli - - zip -r ${VERUS_CLI_WINDOWS} verus-cli - - md5sum ${VERUS_CLI_WINDOWS} > ${VERUS_CLI_WINDOWS}.md5 - - curl -F file=@"${VERUS_CLI_WINDOWS}" - -F channels="${CLI_POST_CHANNEL}" - -F initial_comment="${POST_MESSAGE}" - -H "${SLACK_BOT_AUTH}" - "https://slack.com/api/files.upload" + src/komodo-cli.exe + src/komodo-tx.exe + ${PACKAGE_DIR_WINDOWS} + - zip -r ${PACKAGE_DIR_WINDOWS}.zip ${PACKAGE_DIR_WINDOWS} + - md5sum ${AGAMA_ARTIFACTS_WINDOWS} > ${AGAMA_ARTIFACTS_WINDOWS_CHECKSUM} artifacts: paths: - - ${VERUS_CLI_WINDOWS} - - ${VERUS_CLI_WINDOWS}.md5 + - ${AGAMA_ARTIFACTS_WINDOWS} + - ${AGAMA_ARTIFACTS_WINDOWS_CHECKSUM} expire_in: 1 week ######################################################################################################################## ####END#### WINDOWS ####END#### ######################################################################################################################## ####START#### MACOS ####START#### ######################################################################################################################## -build:mac: +build:macos: stage: build tags: ["High Sierra"] cache: @@ -115,18 +93,13 @@ build:mac: - depends/built script: - zcutil/build-mac.sh -j$(sysctl -n hw.physicalcpu) - - ./makeReleaseMac.sh - - tar -czvf ${VERUS_CLI_MACOS} verus-cli - - md5sum ${VERUS_CLI_MACOS} > ${VERUS_CLI_MACOS}.md5 - - curl -F file=@"${VERUS_CLI_MACOS}" - -F channels="${CLI_POST_CHANNEL}" - -F initial_comment="${POST_MESSAGE}" - -H "${SLACK_BOT_AUTH}" - "https://slack.com/api/files.upload" + - ./makeRelease.sh ${PACKAGE_DIR_MACOS} + - tar -czvf ${AGAMA_ARTIFACTS_MACOS} ${PACKAGE_DIR_MACOS} + - md5sum ${AGAMA_ARTIFACTS_MACOS} > ${AGAMA_ARTIFACTS_MACOS_CHECKSUM} artifacts: paths: - - ${VERUS_CLI_MACOS} - - ${VERUS_CLI_MACOS}.md5 + - ${AGAMA_ARTIFACTS_MACOS} + - ${AGAMA_ARTIFACTS_MACOS_CHECKSUM} expire_in: 1 week ######################################################################################################################## ####END#### MACOS ####END#### @@ -137,13 +110,13 @@ build:mac: ######################################################################################################################## ######################################################################################################################## ######################################################################################################################## -####START#### Test stage: Test functionality of komodo binaries. Produce code quality and SAST reports. ####START#### +####START#### Test stage: Produce code quality and static application security test reports. ####START#### ######################################################################################################################## ######################################################################################################################## ######################################################################################################################## ####START#### Code Quality ####START#### ######################################################################################################################## -.code_quality: +code_quality: image: docker:stable variables: DOCKER_DRIVER: overlay2 @@ -165,7 +138,7 @@ build:mac: ######################################################################################################################## ####START#### Static Application Security Tests ####START#### ######################################################################################################################## -.sast: +sast: image: docker:stable variables: DOCKER_DRIVER: overlay2 @@ -185,149 +158,7 @@ build:mac: ####END#### Static Application Security Tests ####END#### ######################################################################################################################## ######################################################################################################################## -####START#### Run Verus CLI on Ubuntu Xenial (16.04) ####START#### ######################################################################################################################## -.ubuntu:xenial: - image: ubuntu:xenial - variables: - DOCKER_DRIVER: overlay2 - stage: test - before_script: - - apt update && apt install -y wget libgomp1 libcurl4-gnutls-dev python - - rm -rf /root/.komodo || true - - mv .komodo /root/ || true - script: - - tar -xzvf ${VERUS_CLI_LINUX} - - export PATH=$PATH:$CI_PROJECT_DIR/verus-cli - - python qa/verus-cli-tests/verus-cli-tester.py - after_script: - - mv /root/.komodo ./ || true - cache: - key: ${CI_JOB_NAME} - paths: [.komodo] - artifacts: - paths: [log.txt] - expire_in: 1 week - dependencies: - - build:linux -######################################################################################################################## -####END#### Run Verus CLI on Ubuntu Xenial (16.04) ####END#### +####END#### Test stage ####END#### ######################################################################################################################## ######################################################################################################################## -####START#### Run Verus CLI on Ubuntu Bionic (18.04) ####START#### -######################################################################################################################## -.ubuntu:bionic: - image: ubuntu:bionic - variables: - DOCKER_DRIVER: overlay2 - stage: test - before_script: - - apt update && apt install -y wget libgomp1 libcurl4-gnutls-dev python - - rm -rf /root/.komodo || true - - mv .komodo /root/ || true - script: - - tar -xzvf ${VERUS_CLI_LINUX} - - export PATH=$PATH:$CI_PROJECT_DIR/verus-cli - - python qa/verus-cli-tests/verus-cli-tester.py - after_script: - - mv /root/.komodo ./ || true - cache: - key: ${CI_JOB_NAME} - paths: [.komodo] - artifacts: - paths: [log.txt] - expire_in: 1 week - dependencies: - - build:linux -######################################################################################################################## -####END#### Run Verus CLI on Ubuntu Bionic (18.04) ####END#### -######################################################################################################################## -######################################################################################################################## -####START#### Run Verus CLI on MacOS Sierra (10.12.6) ####START#### -######################################################################################################################## -.macos:sierra: - stage: test - tags: ["Sierra"] - script: - - tar -xzvf $VERUS_CLI_MACOS - - export PATH=$PATH:$CI_PROJECT_DIR/verus-cli - - python qa/verus-cli-tests/verus-cli-tester.py - artifacts: - paths: [log.txt] - expire_in: 1 week - dependencies: - - build:mac -######################################################################################################################## -####END#### Run Verus CLI on MacOS Sierra (10.12.6) ####END#### -######################################################################################################################## -######################################################################################################################## -####START#### Run Verus CLI on MacOS High Sierra (10.12.6) ####START#### -######################################################################################################################## -.macos:high-sierra: - stage: test - tags: ["High Sierra"] - script: - - tar -xzvf ${VERUS_CLI_MACOS} - - export PATH=$PATH:$CI_PROJECT_DIR/verus-cli - - python qa/verus-cli-tests/verus-cli-tester.py - artifacts: - paths: [log.txt] - expire_in: 1 week - dependencies: - - build:mac -######################################################################################################################## -####START#### Run Verus CLI on MacOS High Sierra (10.12.6) ####START#### -######################################################################################################################## -######################################################################################################################## -####START#### Run Verus CLI on Windows 10 ####START#### -######################################################################################################################## -.windows:10: - stage: test - tags: ["Windows 10"] - script: - - PowerShell Expand-Archive -Path %VERUS_CLI_WINDOWS% -DestinationPath %CI_PROJECT_DIR% - - set PATH=%PATH%;%CI_PROJECT_DIR%\verus-cli - - qa\verus-cli-tests\verus-cli-tester.py - artifacts: - paths: [log.txt] - expire_in: 1 week - dependencies: - - build:windows -######################################################################################################################## -####END#### Run Verus CLI on Windows 10 ####END#### -######################################################################################################################## -######################################################################################################################## -####END#### Test Stage ####END#### -######################################################################################################################## -######################################################################################################################## -####START#### Deploy ####START#### -######################################################################################################################## -deploy: - stage: deploy - image: google/cloud-sdk:alpine - variables: - DOCKER_DRIVER: overlay2 - dependencies: - - build:linux - - build:windows - - build:mac - script: - - mkdir Windows && mkdir Linux && mkdir MacOS && - mv ${VERUS_CLI_WINDOWS} Windows && - mv ${VERUS_CLI_LINUX} Linux && - mv ${VERUS_CLI_MACOS} MacOS - - echo "$AUTH_KEY" > AUTH_KEY.json && - gcloud auth activate-service-account - --key-file AUTH_KEY.json - - gsutil cp -r Windows Linux MacOS $STAGING/${CI_PROJECT_NAME}/${CI_COMMIT_REF_NAME}/ - - curl -X POST - -F token="$CI_JOB_TOKEN" - -F ref="$DOWNSTREAM_AGAMA_BRANCH" - -F variables\[UPSTREAM_TRIGGER_INFO\]="${POST_MESSAGE}" - -F variables\[VERUS_CLI_LINUX\]="${VERUS_CLI_LINUX}" - -F variables\[VERUS_CLI_WINDOWS\]="${VERUS_CLI_WINDOWS}" - -F variables\[VERUS_CLI_MACOS\]="${VERUS_CLI_MACOS}" - "https://gitlab.com/api/v4/projects/8018592/trigger/pipeline" -######################################################################################################################## -####END#### Deploy ####END#### -######################################################################################################################## diff --git a/README.md b/README.md index bb60c2e6e..9a8582891 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,13 @@ -## VerusCoin version 0.4.0c - -VerusCoin is a new, mineable and stakeable cryptocurrency. It is a live fork of Komodo that retains its Zcash lineage and improves it. VerusCoin will leverage the Komodo platform and dPoW notarization for enhanced security and cross-chain interoperability. We have added a variation of a zawy12, lwma difficulty algorithm, a new CPU-optimized hash algorithm and a new algorithm for fair proof of stake. We describe these changes and vision going forward in a [our Phase I white paper](http://185.25.51.16/papers/VerusPhaseI.pdf) and [our Vision](http://185.25.51.16/papers/VerusVision.pdf). -- [VerusCoin web site https://veruscoin.io/ Wallets and CLI tools](https://veruscoin.io/) -- [VerusCoin Explorer](https://explorer.veruscoin.io/) +[![Build Status](https://travis-ci.org/KomodoPlatform/komodo.svg?branch=dev)](https://travis-ci.org/KomodoPlatform/komodo) +--- +![Komodo Logo](https://i.imgur.com/vIwVtqv.png "Komodo Logo") -## Komodo with Bitcore -This version of Komodo contains Bitcore support for komodo and all its assetchains. +## Komodo -## Komodod -This software is the VerusCoin enhanced Komodo client. Generally, you will use this if you want to mine VRSC or setup a full node. When you run the wallet it launches komodod automatically. On first launch it downloads Zcash parameters, roughly 1GB, which is mildly slow. -The wallet downloads and stores the block chain or asset chain of the coin you select. It downloads and stores the entire history of the coins transactions; depending on the speed of your computer and network connection, the synchronization process could take a day or more once the blockchain has reached a significant size. +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/) @@ -26,23 +23,23 @@ The wallet downloads and stores the block chain or asset chain of the coin you s ## 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% APR on your holdings. -[See this article for more details](https://komodoplatform.atlassian.net/wiki/spaces/KPSD/pages/20480015/Claim+KMD+Interest+in+Agama) +- 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. -- Block Time: 1M 2s -- Block Reward: 3KMD +- Max Supply: 200 million KMD +- Block Time: 1m 2s +- Block Reward: 3 KMD - Mining Algorithm: Equihash ## About this Project @@ -57,86 +54,121 @@ Komodo is based on Zcash and has been extended by our innovative consensus algor 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 ``` +### Build Komodo -Building --------- +This software is based on zcash and considered experimental and is continously undergoing heavy development. -First time you'll need to get assorted startup values downloaded. This takes a moderate amount of time once but then does not need to be repeated unless you bring a new system up. The command is: -``` -./zcutil/fetch-params.sh -``` -Building for Ubunutu/Mint: -``` -./zcutil/build.sh -``` -Building for Mac OS/X (see README-MAC.md): -``` -./zcutil/build-mac.sh -``` -Building for Windows: -``` -./zcutil/build-win.sh -``` -VerusCoin ------- -We develop on dev and some other branches and produce releases of of the master branch, using pull requests to manage what goes into master. The dev branch is considered the bleeding edge codebase, and may even be oncompatible from time to time, while the master-branch is considered tested (unit tests, runtime tests, functionality). At no point of time do the Komodo Platform developers or Verus Developers take any responsbility for any damage out of the usage of this software. - -Verus builds for all operating systems out of the same codebase. Follow the OS specific instructions from below. +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/VerusCoin/VerusCoin -cd VerusCoin -#you might want to: git checkout ; git pull +git clone https://github.com/komodoplatform/komodo --branch master --single-branch +cd komodo ./zcutil/fetch-params.sh # -j8 = using 8 threads for the compilation - replace 8 with number of threads you want to use ./zcutil/build.sh -j8 #This can take some time. ``` -**The VerusCoin enhanced komodo is experimental and a work-in-progress.** Use at your own risk. +#### OSX +Ensure you have [brew](https://brew.sh) and the command line tools installed (comes automatically with XCode) and run: +```shell +brew update && brew install gcc@6 +git clone https://github.com/komodoplatform/komodo --branch master --single-branch +cd komodo +./zcutil/fetch-params.sh +# -j8 = using 8 threads for the compilation - replace 8 with number of threads you want to use +./zcutil/build-mac.sh -j8 +#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 +curl https://sh.rustup.rs -sSf | sh +source $HOME/.cargo/env +rustup target add x86_64-pc-windows-gnu +git clone https://github.com/jl777/komodo --branch master --single-branch +cd komodo +./zcutil/fetch-params.sh +# -j8 = using 8 threads for the compilation - replace 8 with number of threads you want to use +./zcutil/build-win.sh -j8 +#This can take some time. +``` +**komodo is experimental and a work-in-progress.** Use at your own risk. -#To view komodod output: -tail -f ~/.komodo/debug.log -#To view VRSC output: -tail -f ~/.komodo/VRSC/debug.log -Note that this directory is correct for Linux, not Mac or Windows -#To view all command -./src/komodo-cli help -**Zcash is unfinished and highly experimental.** 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` -#### :ledger: Deprecation Policy +#### Create komodo.conf -This release is considered deprecated 16 weeks after the release day. There -is an automatic deprecation shutdown feature which will halt the node some -time after this 16 week time period. The automatic feature is based on block -height. +Create a komodo.conf file: -#Older Komodo Details -The remaining text is from the komodo source we forked when creating VerusCoin/Veruscoin. +``` +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=5.9.102.210 +addnode=78.47.196.146 +addnode=178.63.69.164 +addnode=88.198.65.74 +addnode=5.9.122.241 +addnode=144.76.94.38 +addnode=89.248.166.91 +``` +### 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 +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 -./verusd -exportdir=/tmp & -./verus dumpwallet example -./verus stop -mv ~/.komodo/VRSC ~/.komodo/VRSC.old && mkdir ~/.komodo/VRSC && cp ~/.komodo/VRSC.old/VRSC.conf ~/.komodo/VRSC.old/peers.dat ~/.komodo/VRSC -./verusd -exchange -exportdir=/tmp & -./verus importwallet /tmp/example +./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 ``` --- 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: -The above copyright notices and this permission notice shall be included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/makeReleaseMac.sh b/makeReleaseMac.sh index 2ecf271cc..e20df75aa 100755 --- a/makeReleaseMac.sh +++ b/makeReleaseMac.sh @@ -1,29 +1,19 @@ #!/bin/sh -KMD_DIR=verus-cli -mkdir ${KMD_DIR} - -cp src/fiat/verus \ - src/verusd \ - doc/man/verus-cli/mac/README.txt \ - zcutil/fetch-params.sh \ - verus-cli -mv verus-cli/fetch-params.sh verus-cli/fetch-params -chmod +x ${KMD_DIR}/fetch-params -chmod +x ${KMD_DIR}/verus -chmod +x ${KMD_DIR}/verusd +PACKAGE_DIR="$@" +mkdir ${PACKAGE_DIR} binaries=("komodo-cli" "komodod") alllibs=() for binary in "${binaries[@]}"; do # do the work in the destination directory - cp src/${binary} ${KMD_DIR} + cp src/${binary} ${PACKAGE_DIR} # find the dylibs to copy for komodod - DYLIBS=`otool -L ${KMD_DIR}/${binary} | grep "/usr/local" | awk -F' ' '{ print $1 }'` - echo "copying ${DYLIBS} to ${KMD_DIR}" + DYLIBS=`otool -L ${PACKAGE_DIR}/${binary} | grep "/usr/local" | awk -F' ' '{ print $1 }'` + echo "copying ${DYLIBS} to ${PACKAGE_DIR}" # copy the dylibs to the srcdir - for dylib in ${DYLIBS}; do cp -rf ${dylib} ${KMD_DIR}; done + for dylib in ${DYLIBS}; do cp -rf ${dylib} ${PACKAGE_DIR}; done done libraries=("libgcc_s.1.dylib" "libgomp.1.dylib" "libidn2.0.dylib" "libstdc++.6.dylib") @@ -31,10 +21,10 @@ libraries=("libgcc_s.1.dylib" "libgomp.1.dylib" "libidn2.0.dylib" "libstdc++.6.d for binary in "${libraries[@]}"; do # find the dylibs to copy for komodod - DYLIBS=`otool -L ${KMD_DIR}/${binary} | grep "/usr/local" | awk -F' ' '{ print $1 }'` - echo "copying ${DYLIBS} to ${KMD_DIR}" + DYLIBS=`otool -L ${PACKAGE_DIR}/${binary} | grep "/usr/local" | awk -F' ' '{ print $1 }'` + echo "copying ${DYLIBS} to ${PACKAGE_DIR}" # copy the dylibs to the srcdir - for dylib in ${DYLIBS}; do cp -rf ${dylib} ${KMD_DIR}; alllibs+=(${dylib}); done + for dylib in ${DYLIBS}; do cp -rf ${dylib} ${PACKAGE_DIR}; alllibs+=(${dylib}); done done indirectlibraries=("libintl.8.dylib" "libunistring.2.dylib") @@ -44,10 +34,10 @@ do # Need to undo this for the dylibs when we are done chmod 755 src/${binary} # find the dylibs to copy for komodod - DYLIBS=`otool -L ${KMD_DIR}/${binary} | grep "/usr/local" | awk -F' ' '{ print $1 }'` - echo "copying indirect ${DYLIBS} to ${KMD_DIR}" + DYLIBS=`otool -L ${PACKAGE_DIR}/${binary} | grep "/usr/local" | awk -F' ' '{ print $1 }'` + echo "copying indirect ${DYLIBS} to ${PACKAGE_DIR}" # copy the dylibs to the dest dir - for dylib in ${DYLIBS}; do cp -rf ${dylib} ${KMD_DIR}; alllibs+=(${dylib}); done + for dylib in ${DYLIBS}; do cp -rf ${dylib} ${PACKAGE_DIR}; alllibs+=(${dylib}); done done for binary in "${binaries[@]}"; @@ -57,9 +47,9 @@ do for dylib in "${alllibs[@]}" do echo "Next lib is ${dylib} " - install_name_tool -change ${dylib} @executable_path/`basename ${dylib}` ${KMD_DIR}/${binary} + install_name_tool -change ${dylib} @executable_path/`basename ${dylib}` ${PACKAGE_DIR}/${binary} done - chmod +x ${KMD_DIR}/${binary} + chmod +x ${PACKAGE_DIR}/${binary} done for binary in "${libraries[@]}"; @@ -69,8 +59,6 @@ do for dylib in "${alllibs[@]}" do echo "Next lib is ${dylib} " - install_name_tool -change ${dylib} @executable_path/`basename ${dylib}` ${KMD_DIR}/${binary} + install_name_tool -change ${dylib} @executable_path/`basename ${dylib}` ${PACKAGE_DIR}/${binary} done done - - diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index bc83f9880..0d7d5509c 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -14,6 +14,11 @@ testScripts=( 'ac_private.py' 'verushash.py' 'cryptoconditions.py' + 'cryptoconditions_dice.py' + 'cryptoconditions_faucet.py' + 'cryptoconditions_oracles.py' + 'cryptoconditions_rewards.py' + 'cryptoconditions_token.py' 'paymentdisclosure.py' 'prioritisetransaction.py' 'wallet_treestate.py' diff --git a/qa/rpc-tests/cryptoconditions_dice.py b/qa/rpc-tests/cryptoconditions_dice.py new file mode 100755 index 000000000..9957a8f2b --- /dev/null +++ b/qa/rpc-tests/cryptoconditions_dice.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 SuperNET developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_greater_than, \ + initialize_chain_clean, initialize_chain, start_nodes, start_node, connect_nodes_bi, \ + stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises +from cryptoconditions import assert_success, assert_error, generate_random_string + +class CryptoconditionsDiceTest(BitcoinTestFramework): + + def setup_chain(self): + print("Initializing CC test directory "+self.options.tmpdir) + self.num_nodes = 2 + initialize_chain_clean(self.options.tmpdir, self.num_nodes) + + def setup_network(self, split = False): + print("Setting up network...") + self.addr = "RWPg8B91kfK5UtUN7z6s6TeV9cHSGtVY8D" + self.pubkey = "02676d00110c2cd14ae24f95969e8598f7ccfaa675498b82654a5b5bd57fc1d8cf" + self.privkey = "UqMgxk7ySPNQ4r9nKAFPjkXy6r5t898yhuNCjSZJLg3RAM4WW1m9" + self.addr1 = "RXEXoa1nRmKhMbuZovpcYwQMsicwzccZBp" + self.pubkey1 = "024026d4ad4ecfc1f705a9b42ca64af6d2ad947509c085534a30b8861d756c6ff0" + self.privkey1 = "UtdydP56pGTFmawHzHr1wDrc4oUwCNW1ttX8Pc3KrvH3MA8P49Wi" + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, + extra_args=[[ + # always give -ac_name as first extra_arg and port as third + '-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node0/REGTEST.conf', + '-port=64367', + '-rpcport=64368', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt' + ], + ['-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node1/REGTEST.conf', + '-port=64365', + '-rpcport=64366', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey1, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '-addnode=127.0.0.1:64367', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt']] + ) + self.is_network_split = split + self.rpc = self.nodes[0] + self.rpc1 = self.nodes[1] + self.sync_all() + print("Done setting up network") + + def send_and_mine(self, xtn, rpc_connection): + txid = rpc_connection.sendrawtransaction(xtn) + assert txid, 'got txid' + # we need the tx above to be confirmed in the next block + rpc_connection.generate(1) + return txid + + def run_dice_tests(self): + rpc = self.nodes[0] + rpc1 = self.nodes[1] + self.sync_all() + + # have to generate few blocks on second node to be able to place bets + rpc1.generate(10) + result = rpc1.getbalance() + assert_greater_than(result, 100000) + + dice = rpc.diceaddress() + assert_equal(dice['result'], 'success') + for x in ['myCCaddress', 'DiceCCaddress', 'Dicemarker', 'myaddress']: + assert_equal(dice[x][0], 'R') + + dice = rpc.diceaddress(self.pubkey) + assert_equal(dice['result'], 'success') + for x in ['myCCaddress', 'DiceCCaddress', 'Dicemarker', 'myaddress', 'CCaddress']: + assert_equal(dice[x][0], 'R') + + # no dice created yet + result = rpc.dicelist() + assert_equal(result, []) + + # creating dice plan with too long name (>8 chars) + result = rpc.dicefund("THISISTOOLONG", "10000", "10", "10000", "10", "5") + assert_error(result) + + # creating dice plan with < 100 funding + result = rpc.dicefund("LUCKY","10","1","10000","10","5") + assert_error(result) + + # creating dice plan with 0 blocks timeout + result = rpc.dicefund("LUCKY","10","1","10000","10","0") + assert_error(result) + + # creating dice plan + dicefundtx = rpc.dicefund("LUCKY","1000","1","800","10","5") + diceid = self.send_and_mine(dicefundtx['hex'], rpc) + + # checking if it in plans list now + result = rpc.dicelist() + assert_equal(result[0], diceid) + + # set dice name for futher usage + dicename = "LUCKY" + + # adding zero funds to plan + result = rpc.diceaddfunds(dicename,diceid,"0") + assert_error(result) + + # adding negative funds to plan + result = rpc.diceaddfunds(dicename,diceid,"-1") + assert_error(result) + + # adding funds to plan + addfundstx = rpc.diceaddfunds(dicename,diceid,"1100") + result = self.send_and_mine(addfundstx['hex'], rpc) + + # checking if funds added to plan + result = rpc.diceinfo(diceid) + assert_equal(result["funding"], "2100.00000000") + + # not valid dice info checking + result = rpc.diceinfo("invalid") + assert_error(result) + + # placing 0 amount bet + result = rpc1.dicebet(dicename,diceid,"0","2") + assert_error(result) + + # placing negative amount bet + result = rpc1.dicebet(dicename,diceid,"-1","2") + assert_error(result) + + # placing bet more than maxbet + result = rpc1.dicebet(dicename,diceid,"900","2") + assert_error(result) + + # placing bet with amount more than funding + result = rpc1.dicebet(dicename,diceid,"3000","2") + assert_error(result) + + # placing bet with potential won more than funding + result = rpc1.dicebet(dicename,diceid,"750","9") + assert_error(result) + + # placing 0 odds bet + result = rpc1.dicebet(dicename,diceid,"1","0") + assert_error(result) + + # placing negative odds bet + result = rpc1.dicebet(dicename,diceid,"1","-1") + assert_error(result) + + # placing bet with odds more than allowed + result = rpc1.dicebet(dicename,diceid,"1","11") + assert_error(result) + + # placing bet with not correct dice name + result = rpc1.dicebet("nope",diceid,"100","2") + assert_error(result) + + # placing bet with not correct dice id + result = rpc1.dicebet(dicename,self.pubkey,"100","2") + assert_error(result) + + # have to make some entropy for the next test + entropytx = 0 + fundingsum = 1 + while entropytx < 110: + fundingsuminput = str(fundingsum) + fundinghex = rpc.diceaddfunds(dicename,diceid,fundingsuminput) + result = self.send_and_mine(fundinghex['hex'], rpc) + entropytx = entropytx + 1 + fundingsum = fundingsum + 1 + + rpc.generate(2) + self.sync_all() + + # valid bet placing + placebet = rpc1.dicebet(dicename,diceid,"100","2") + betid = self.send_and_mine(placebet["hex"], rpc1) + assert result, "bet placed" + + # check bet status + result = rpc1.dicestatus(dicename,diceid,betid) + assert_success(result) + + # note initial dice funding state at this point. + # TODO: track player balance somehow (hard to do because of mining and fees) + diceinfo = rpc.diceinfo(diceid) + funding = float(diceinfo['funding']) + + # # placing same amount bets with amount 1 and odds 1:3, checking if balance changed correct + # losscounter = 0 + # wincounter = 0 + # betcounter = 0 + # + # while (betcounter < 10): + # placebet = rpc1.dicebet(dicename,diceid,"1","2") + # betid = self.send_and_mine(placebet["hex"], rpc1) + # time.sleep(3) + # self.sync_all() + # finish = rpc.dicefinish(dicename,diceid,betid) + # self.send_and_mine(finish["hex"], rpc1) + # self.sync_all() + # time.sleep(3) + # betresult = rpc1.dicestatus(dicename,diceid,betid) + # betcounter = betcounter + 1 + # if betresult["status"] == "loss": + # losscounter = losscounter + 1 + # elif betresult["status"] == "win": + # wincounter = wincounter + 1 + # else: + # pass + # + # # funding balance should increase if player loss, decrease if player won + # fundbalanceguess = funding + losscounter - wincounter * 2 + # fundinfoactual = rpc.diceinfo(diceid) + # assert_equal(round(fundbalanceguess),round(float(fundinfoactual['funding']))) + + def run_test(self): + print("Mining blocks...") + rpc = self.nodes[0] + rpc1 = self.nodes[1] + # utxos from block 1 become mature in block 101 + rpc.generate(101) + self.sync_all() + rpc.getinfo() + rpc1.getinfo() + # this corresponds to -pubkey above + print("Importing privkeys") + rpc.importprivkey(self.privkey) + rpc1.importprivkey(self.privkey1) + self.run_dice_tests() + + +if __name__ == '__main__': + CryptoconditionsDiceTest ().main() diff --git a/qa/rpc-tests/cryptoconditions_faucet.py b/qa/rpc-tests/cryptoconditions_faucet.py new file mode 100755 index 000000000..4f8cb42cf --- /dev/null +++ b/qa/rpc-tests/cryptoconditions_faucet.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 SuperNET developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_greater_than, \ + initialize_chain_clean, initialize_chain, start_nodes, start_node, connect_nodes_bi, \ + stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises +from cryptoconditions import assert_success, assert_error, generate_random_string + + +class CryptoconditionsFaucetTest(BitcoinTestFramework): + + def setup_chain(self): + print("Initializing CC test directory "+self.options.tmpdir) + self.num_nodes = 2 + initialize_chain_clean(self.options.tmpdir, self.num_nodes) + + def setup_network(self, split = False): + print("Setting up network...") + self.addr = "RWPg8B91kfK5UtUN7z6s6TeV9cHSGtVY8D" + self.pubkey = "02676d00110c2cd14ae24f95969e8598f7ccfaa675498b82654a5b5bd57fc1d8cf" + self.privkey = "UqMgxk7ySPNQ4r9nKAFPjkXy6r5t898yhuNCjSZJLg3RAM4WW1m9" + self.addr1 = "RXEXoa1nRmKhMbuZovpcYwQMsicwzccZBp" + self.pubkey1 = "024026d4ad4ecfc1f705a9b42ca64af6d2ad947509c085534a30b8861d756c6ff0" + self.privkey1 = "UtdydP56pGTFmawHzHr1wDrc4oUwCNW1ttX8Pc3KrvH3MA8P49Wi" + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, + extra_args=[[ + # always give -ac_name as first extra_arg and port as third + '-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node0/REGTEST.conf', + '-port=64367', + '-rpcport=64368', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt' + ], + ['-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node1/REGTEST.conf', + '-port=64365', + '-rpcport=64366', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey1, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '-addnode=127.0.0.1:64367', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt']] + ) + self.is_network_split = split + self.rpc = self.nodes[0] + self.rpc1 = self.nodes[1] + self.sync_all() + print("Done setting up network") + + def send_and_mine(self, xtn, rpc_connection): + txid = rpc_connection.sendrawtransaction(xtn) + assert txid, 'got txid' + # we need the tx above to be confirmed in the next block + rpc_connection.generate(1) + return txid + + def run_faucet_tests(self): + rpc = self.rpc + rpc1 = self.rpc1 + + # basic sanity tests + result = rpc.getwalletinfo() + assert_greater_than(result['txcount'], 100) + assert_greater_than(result['balance'], 0.0) + balance = result['balance'] + + faucet = rpc.faucetaddress() + assert_equal(faucet['result'], 'success') + # verify all keys look like valid AC addrs, could be better + for x in ['myCCaddress', 'FaucetCCaddress', 'Faucetmarker', 'myaddress']: + assert_equal(faucet[x][0], 'R') + + result = rpc.faucetaddress(self.pubkey) + assert_success(result) + # test that additional CCaddress key is returned + for x in ['myCCaddress', 'FaucetCCaddress', 'Faucetmarker', 'myaddress', 'CCaddress']: + assert_equal(result[x][0], 'R') + + # no funds in the faucet yet + result = rpc.faucetget() + assert_error(result) + + result = rpc.faucetinfo() + assert_success(result) + + result = rpc.faucetfund("0") + assert_error(result) + + result = rpc.faucetfund("-1") + assert_error(result) + + # we need at least 1 + txfee to get + result = rpc.faucetfund("2") + assert_success(result) + assert result['hex'], "hex key found" + + # broadcast the xtn + result = rpc.sendrawtransaction(result['hex']) + txid = result[0] + assert txid, "found txid" + + # we need the tx above to be confirmed in the next block + rpc.generate(1) + self.sync_all() + + result = rpc.getwalletinfo() + # minus one block reward + balance2 = result['balance'] - 100000 + # make sure our balance is less now + assert_greater_than(balance, balance2) + + result = rpc.faucetinfo() + assert_success(result) + assert_greater_than( result['funding'], 0 ) + + # claiming faucet on second node + faucetgethex = rpc1.faucetget() + assert_success(faucetgethex) + assert faucetgethex['hex'], "hex key found" + + balance1 = rpc1.getwalletinfo()['balance'] + + # try to broadcast the faucetget transaction + result = self.send_and_mine(faucetgethex['hex'], rpc1) + assert txid, "transaction broadcasted" + + balance2 = rpc1.getwalletinfo()['balance'] + assert_greater_than(balance2, balance1) + + self.sync_all() + + def run_test(self): + print("Mining blocks...") + rpc = self.nodes[0] + rpc1 = self.nodes[1] + # utxos from block 1 become mature in block 101 + rpc.generate(101) + self.sync_all() + rpc.getinfo() + rpc1.getinfo() + # this corresponds to -pubkey above + print("Importing privkeys") + rpc.importprivkey(self.privkey) + rpc1.importprivkey(self.privkey1) + self.run_faucet_tests() + + +if __name__ == '__main__': + CryptoconditionsFaucetTest ().main() diff --git a/qa/rpc-tests/cryptoconditions_oracles.py b/qa/rpc-tests/cryptoconditions_oracles.py new file mode 100755 index 000000000..31712abbc --- /dev/null +++ b/qa/rpc-tests/cryptoconditions_oracles.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 SuperNET developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_greater_than, \ + initialize_chain_clean, initialize_chain, start_nodes, start_node, connect_nodes_bi, \ + stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises +from cryptoconditions import assert_success, assert_error, generate_random_string + + +class CryptoconditionsOraclesTest(BitcoinTestFramework): + + + def setup_chain(self): + print("Initializing CC test directory "+self.options.tmpdir) + self.num_nodes = 2 + initialize_chain_clean(self.options.tmpdir, self.num_nodes) + + def setup_network(self, split = False): + print("Setting up network...") + self.addr = "RWPg8B91kfK5UtUN7z6s6TeV9cHSGtVY8D" + self.pubkey = "02676d00110c2cd14ae24f95969e8598f7ccfaa675498b82654a5b5bd57fc1d8cf" + self.privkey = "UqMgxk7ySPNQ4r9nKAFPjkXy6r5t898yhuNCjSZJLg3RAM4WW1m9" + self.addr1 = "RXEXoa1nRmKhMbuZovpcYwQMsicwzccZBp" + self.pubkey1 = "024026d4ad4ecfc1f705a9b42ca64af6d2ad947509c085534a30b8861d756c6ff0" + self.privkey1 = "UtdydP56pGTFmawHzHr1wDrc4oUwCNW1ttX8Pc3KrvH3MA8P49Wi" + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, + extra_args=[[ + # always give -ac_name as first extra_arg and port as third + '-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node0/REGTEST.conf', + '-port=64367', + '-rpcport=64368', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt' + ], + ['-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node1/REGTEST.conf', + '-port=64365', + '-rpcport=64366', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey1, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '-addnode=127.0.0.1:64367', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt']] + ) + self.is_network_split = split + self.rpc = self.nodes[0] + self.rpc1 = self.nodes[1] + self.sync_all() + print("Done setting up network") + + def send_and_mine(self, xtn, rpc_connection): + txid = rpc_connection.sendrawtransaction(xtn) + assert txid, 'got txid' + # we need the tx above to be confirmed in the next block + rpc_connection.generate(1) + return txid + + def run_oracles_tests(self): + rpc = self.nodes[0] + rpc1 = self.nodes[1] + + result = rpc1.oraclesaddress() + + result = rpc.oraclesaddress() + assert_success(result) + for x in ['OraclesCCaddress', 'Oraclesmarker', 'myCCaddress', 'myaddress']: + assert_equal(result[x][0], 'R') + + result = rpc.oraclesaddress(self.pubkey) + assert_success(result) + for x in ['OraclesCCaddress', 'Oraclesmarker', 'myCCaddress', 'myaddress']: + assert_equal(result[x][0], 'R') + + # there are no oracles created yet + result = rpc.oracleslist() + assert_equal(result, []) + + # looking up non-existent oracle should return error. + result = rpc.oraclesinfo("none") + assert_error(result) + + # attempt to create oracle with not valid data type should return error + result = rpc.oraclescreate("Test", "Test", "Test") + assert_error(result) + + # attempt to create oracle with description > 32 symbols should return error + too_long_name = generate_random_string(33) + result = rpc.oraclescreate(too_long_name, "Test", "s") + + # attempt to create oracle with description > 4096 symbols should return error + too_long_description = generate_random_string(4100) + result = rpc.oraclescreate("Test", too_long_description, "s") + assert_error(result) + # valid creating oracles of different types + # using such naming to re-use it for data publishing / reading (e.g. oracle_s for s type) + valid_formats = ["s", "S", "d", "D", "c", "C", "t", "T", "i", "I", "l", "L", "h", "Ihh"] + for f in valid_formats: + result = rpc.oraclescreate("Test", "Test", f) + assert_success(result) + globals()["oracle_{}".format(f)] = self.send_and_mine(result['hex'], rpc) + + # trying to register with negative datafee + for f in valid_formats: + result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "-100") + assert_error(result) + + # trying to register with zero datafee + for f in valid_formats: + result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "0") + assert_error(result) + + # trying to register with datafee less than txfee + for f in valid_formats: + result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "500") + assert_error(result) + + # trying to register valid + for f in valid_formats: + result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "10000") + assert_success(result) + register_txid = self.send_and_mine(result["hex"], rpc) + assert register_txid, "got txid" + + # TODO: for most of the non valid oraclesregister and oraclessubscribe transactions generating and broadcasting now + # so trying only valid oraclessubscribe atm + for f in valid_formats: + result = rpc.oraclessubscribe(globals()["oracle_{}".format(f)], self.pubkey, "1") + assert_success(result) + subscribe_txid = self.send_and_mine(result["hex"], rpc) + assert register_txid, "got txid" + + # now lets publish and read valid data for each oracle type + + # s type + result = rpc.oraclesdata(globals()["oracle_{}".format("s")], "05416e746f6e") + assert_success(result) + # baton + oraclesdata_s = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("s")], oraclesdata_s, "1") + assert_equal("[u'Anton']", str(result["samples"][0]), "Data match") + + # S type + result = rpc.oraclesdata(globals()["oracle_{}".format("S")], "000161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161") + assert_success(result) + # baton + oraclesdata_S = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("S")], oraclesdata_S, "1") + assert_equal("[u'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa']", str(result["samples"][0]), "Data match") + + # d type + result = rpc.oraclesdata(globals()["oracle_{}".format("d")], "0101") + assert_success(result) + # baton + oraclesdata_d = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("d")], oraclesdata_d, "1") + # TODO: working not correct now! + #assert_equal("[u'01']", str(result["samples"][0]), "Data match") + + # D type + result = rpc.oraclesdata(globals()["oracle_{}".format("D")], "0101") + assert_success(result) + # baton + oraclesdata_D = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("D")], oraclesdata_D, "1") + # TODO: working not correct now! + #assert_equal("[u'01']", str(result["samples"][0]), "Data match") + + # c type + result = rpc.oraclesdata(globals()["oracle_{}".format("c")], "ff") + assert_success(result) + # baton + oraclesdata_c = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("c")], oraclesdata_c, "1") + assert_equal("[u'-1']", str(result["samples"][0]), "Data match") + + # C type + result = rpc.oraclesdata(globals()["oracle_{}".format("C")], "ff") + assert_success(result) + # baton + oraclesdata_C = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("C")], oraclesdata_C, "1") + assert_equal("[u'255']", str(result["samples"][0]), "Data match") + + # t type + result = rpc.oraclesdata(globals()["oracle_{}".format("t")], "ffff") + assert_success(result) + # baton + oraclesdata_t = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("t")], oraclesdata_t, "1") + assert_equal("[u'-1']", str(result["samples"][0]), "Data match") + + # T type + result = rpc.oraclesdata(globals()["oracle_{}".format("T")], "ffff") + assert_success(result) + # baton + oraclesdata_T = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("T")], oraclesdata_T, "1") + assert_equal("[u'65535']", str(result["samples"][0]), "Data match") + + # i type + result = rpc.oraclesdata(globals()["oracle_{}".format("i")], "ffffffff") + assert_success(result) + # baton + oraclesdata_i = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("i")], oraclesdata_i, "1") + assert_equal("[u'-1']", str(result["samples"][0]), "Data match") + + # I type + result = rpc.oraclesdata(globals()["oracle_{}".format("I")], "ffffffff") + assert_success(result) + # baton + oraclesdata_I = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("I")], oraclesdata_I, "1") + assert_equal("[u'4294967295']", str(result["samples"][0]), "Data match") + + # l type + result = rpc.oraclesdata(globals()["oracle_{}".format("l")], "00000000ffffffff") + assert_success(result) + # baton + oraclesdata_l = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("l")], oraclesdata_l, "1") + # TODO: working not correct now! + #assert_equal("[u'-4294967296']", str(result["samples"][0]), "Data match") + + # L type + result = rpc.oraclesdata(globals()["oracle_{}".format("L")], "00000000ffffffff") + assert_success(result) + # baton + oraclesdata_L = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("L")], oraclesdata_L, "1") + assert_equal("[u'18446744069414584320']", str(result["samples"][0]), "Data match") + + # h type + result = rpc.oraclesdata(globals()["oracle_{}".format("h")], "00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff") + assert_success(result) + # baton + oraclesdata_h = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("h")], oraclesdata_h, "1") + assert_equal("[u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000']", str(result["samples"][0]), "Data match") + + # Ihh type + result = rpc.oraclesdata(globals()["oracle_{}".format("Ihh")], "00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff") + assert_success(result) + # baton + oraclesdata_Ihh = self.send_and_mine(result["hex"], rpc) + result = rpc.oraclessamples(globals()["oracle_{}".format("Ihh")], oraclesdata_Ihh, "1") + assert_equal("[u'0']", str(result["samples"][0]), "Data match") + assert_equal("[u'00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff']", str(result["samples"][1]), "Data match") + assert_equal("[u'00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff']", str(result["samples"][2]), "Data match") + + + def run_test(self): + print("Mining blocks...") + rpc = self.nodes[0] + rpc1 = self.nodes[1] + # utxos from block 1 become mature in block 101 + rpc.generate(101) + self.sync_all() + rpc.getinfo() + rpc1.getinfo() + # this corresponds to -pubkey above + print("Importing privkeys") + rpc.importprivkey(self.privkey) + rpc1.importprivkey(self.privkey1) + self.run_oracles_tests() + + +if __name__ == '__main__': + CryptoconditionsOraclesTest().main() diff --git a/qa/rpc-tests/cryptoconditions_rewards.py b/qa/rpc-tests/cryptoconditions_rewards.py new file mode 100755 index 000000000..5538a399c --- /dev/null +++ b/qa/rpc-tests/cryptoconditions_rewards.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 SuperNET developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_greater_than, \ + initialize_chain_clean, initialize_chain, start_nodes, start_node, connect_nodes_bi, \ + stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises +from cryptoconditions import assert_success, assert_error, generate_random_string + + +class CryptoconditionsRewardsTest(BitcoinTestFramework): + + + def setup_chain(self): + print("Initializing CC test directory "+self.options.tmpdir) + self.num_nodes = 2 + initialize_chain_clean(self.options.tmpdir, self.num_nodes) + + def setup_network(self, split = False): + print("Setting up network...") + self.addr = "RWPg8B91kfK5UtUN7z6s6TeV9cHSGtVY8D" + self.pubkey = "02676d00110c2cd14ae24f95969e8598f7ccfaa675498b82654a5b5bd57fc1d8cf" + self.privkey = "UqMgxk7ySPNQ4r9nKAFPjkXy6r5t898yhuNCjSZJLg3RAM4WW1m9" + self.addr1 = "RXEXoa1nRmKhMbuZovpcYwQMsicwzccZBp" + self.pubkey1 = "024026d4ad4ecfc1f705a9b42ca64af6d2ad947509c085534a30b8861d756c6ff0" + self.privkey1 = "UtdydP56pGTFmawHzHr1wDrc4oUwCNW1ttX8Pc3KrvH3MA8P49Wi" + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, + extra_args=[[ + # always give -ac_name as first extra_arg and port as third + '-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node0/REGTEST.conf', + '-port=64367', + '-rpcport=64368', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt' + ], + ['-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node1/REGTEST.conf', + '-port=64365', + '-rpcport=64366', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey1, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '-addnode=127.0.0.1:64367', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt']] + ) + self.is_network_split = split + self.rpc = self.nodes[0] + self.rpc1 = self.nodes[1] + self.sync_all() + print("Done setting up network") + + def send_and_mine(self, xtn, rpc_connection): + txid = rpc_connection.sendrawtransaction(xtn) + assert txid, 'got txid' + # we need the tx above to be confirmed in the next block + rpc_connection.generate(1) + return txid + + def run_rewards_tests(self): + rpc = self.nodes[0] + result = rpc.rewardsaddress() + for x in ['RewardsCCaddress', 'myCCaddress', 'Rewardsmarker', 'myaddress']: + assert_equal(result[x][0], 'R') + + result = rpc.rewardsaddress(self.pubkey) + for x in ['RewardsCCaddress', 'myCCaddress', 'Rewardsmarker', 'myaddress', 'CCaddress']: + assert_equal(result[x][0], 'R') + + # no rewards yet + result = rpc.rewardslist() + assert_equal(result, []) + + # looking up non-existent reward should return error + result = rpc.rewardsinfo("none") + assert_error(result) + + # creating rewards plan with name > 8 chars, should return error + result = rpc.rewardscreatefunding("STUFFSTUFF", "7777", "25", "0", "10", "10") + assert_error(result) + + # creating rewards plan with 0 funding + result = rpc.rewardscreatefunding("STUFF", "0", "25", "0", "10", "10") + assert_error(result) + + # creating rewards plan with 0 maxdays + result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "0") + assert_error(result) + + # creating rewards plan with > 25% APR + result = rpc.rewardscreatefunding("STUFF", "7777", "30", "0", "10", "10") + assert_error(result) + + # creating valid rewards plan + result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10") + assert result['hex'], 'got raw xtn' + fundingtxid = rpc.sendrawtransaction(result['hex']) + assert fundingtxid, 'got txid' + + # confirm the above xtn + rpc.generate(1) + result = rpc.rewardsinfo(fundingtxid) + assert_success(result) + assert_equal(result['name'], 'STUFF') + assert_equal(result['APR'], "25.00000000") + assert_equal(result['minseconds'], 0) + assert_equal(result['maxseconds'], 864000) + assert_equal(result['funding'], "7777.00000000") + assert_equal(result['mindeposit'], "10.00000000") + assert_equal(result['fundingtxid'], fundingtxid) + + # checking if new plan in rewardslist + result = rpc.rewardslist() + assert_equal(result[0], fundingtxid) + + # creating reward plan with already existing name, should return error + result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10") + assert_error(result) + + # add funding amount must be positive + result = rpc.rewardsaddfunding("STUFF", fundingtxid, "-1") + assert_error(result) + + # add funding amount must be positive + result = rpc.rewardsaddfunding("STUFF", fundingtxid, "0") + assert_error(result) + + # adding valid funding + result = rpc.rewardsaddfunding("STUFF", fundingtxid, "555") + addfundingtxid = self.send_and_mine(result['hex'], rpc) + assert addfundingtxid, 'got funding txid' + + # checking if funding added to rewardsplan + result = rpc.rewardsinfo(fundingtxid) + assert_equal(result['funding'], "8332.00000000") + + # trying to lock funds, locking funds amount must be positive + result = rpc.rewardslock("STUFF", fundingtxid, "-5") + assert_error(result) + + # trying to lock funds, locking funds amount must be positive + result = rpc.rewardslock("STUFF", fundingtxid, "0") + assert_error(result) + + # trying to lock less than the min amount is an error + result = rpc.rewardslock("STUFF", fundingtxid, "7") + assert_error(result) + + # locking funds in rewards plan + result = rpc.rewardslock("STUFF", fundingtxid, "10") + assert_success(result) + locktxid = result['hex'] + assert locktxid, "got lock txid" + + # locktxid has not been broadcast yet + result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid) + assert_error(result) + + # broadcast xtn + txid = rpc.sendrawtransaction(locktxid) + assert txid, 'got txid from sendrawtransaction' + + # confirm the xtn above + rpc.generate(1) + + # will not unlock since reward amount is less than tx fee + result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid) + assert_error(result) + + def run_test(self): + print("Mining blocks...") + rpc = self.nodes[0] + rpc1 = self.nodes[1] + # utxos from block 1 become mature in block 101 + rpc.generate(101) + self.sync_all() + rpc.getinfo() + rpc1.getinfo() + # this corresponds to -pubkey above + print("Importing privkeys") + rpc.importprivkey(self.privkey) + rpc1.importprivkey(self.privkey1) + self.run_rewards_tests() + +if __name__ == '__main__': + CryptoconditionsRewardsTest().main() diff --git a/qa/rpc-tests/cryptoconditions_token.py b/qa/rpc-tests/cryptoconditions_token.py new file mode 100755 index 000000000..8120f23bf --- /dev/null +++ b/qa/rpc-tests/cryptoconditions_token.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 SuperNET developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_greater_than, \ + initialize_chain_clean, initialize_chain, start_nodes, start_node, connect_nodes_bi, \ + stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises +from cryptoconditions import assert_success, assert_error, generate_random_string + + +class CryptoconditionsTokenTest(BitcoinTestFramework): + + + def setup_chain(self): + print("Initializing CC test directory "+self.options.tmpdir) + self.num_nodes = 2 + initialize_chain_clean(self.options.tmpdir, self.num_nodes) + + def setup_network(self, split = False): + print("Setting up network...") + self.addr = "RWPg8B91kfK5UtUN7z6s6TeV9cHSGtVY8D" + self.pubkey = "02676d00110c2cd14ae24f95969e8598f7ccfaa675498b82654a5b5bd57fc1d8cf" + self.privkey = "UqMgxk7ySPNQ4r9nKAFPjkXy6r5t898yhuNCjSZJLg3RAM4WW1m9" + self.addr1 = "RXEXoa1nRmKhMbuZovpcYwQMsicwzccZBp" + self.pubkey1 = "024026d4ad4ecfc1f705a9b42ca64af6d2ad947509c085534a30b8861d756c6ff0" + self.privkey1 = "UtdydP56pGTFmawHzHr1wDrc4oUwCNW1ttX8Pc3KrvH3MA8P49Wi" + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, + extra_args=[[ + # always give -ac_name as first extra_arg and port as third + '-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node0/REGTEST.conf', + '-port=64367', + '-rpcport=64368', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt' + ], + ['-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node1/REGTEST.conf', + '-port=64365', + '-rpcport=64366', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey1, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '-addnode=127.0.0.1:64367', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt']] + ) + self.is_network_split = split + self.rpc = self.nodes[0] + self.rpc1 = self.nodes[1] + self.sync_all() + print("Done setting up network") + + def send_and_mine(self, xtn, rpc_connection): + txid = rpc_connection.sendrawtransaction(xtn) + assert txid, 'got txid' + # we need the tx above to be confirmed in the next block + rpc_connection.generate(1) + return txid + + def run_token_tests(self): + rpc = self.nodes[0] + result = rpc.tokenaddress() + assert_success(result) + for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress']: + assert_equal(result[x][0], 'R') + + result = rpc.tokenaddress(self.pubkey) + assert_success(result) + for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress', 'CCaddress']: + assert_equal(result[x][0], 'R') + # there are no tokens created yet + result = rpc.tokenlist() + assert_equal(result, []) + + # trying to create token with negaive supply + result = rpc.tokencreate("NUKE", "-1987420", "no bueno supply") + assert_error(result) + + # creating token with name more than 32 chars + result = rpc.tokencreate("NUKE123456789012345678901234567890", "1987420", "name too long") + assert_error(result) + + # creating valid token + result = rpc.tokencreate("DUKE", "1987.420", "Duke's custom token") + assert_success(result) + + tokenid = self.send_and_mine(result['hex'], rpc) + + result = rpc.tokenlist() + assert_equal(result[0], tokenid) + + # there are no token orders yet + result = rpc.tokenorders() + assert_equal(result, []) + + # getting token balance for pubkey + result = rpc.tokenbalance(self.pubkey) + assert_success(result) + assert_equal(result['balance'], 0) + assert_equal(result['CCaddress'], 'RCRsm3VBXz8kKTsYaXKpy7pSEzrtNNQGJC') + assert_equal(result['tokenid'], self.pubkey) + + # get token balance for token with pubkey + result = rpc.tokenbalance(tokenid, self.pubkey) + assert_success(result) + assert_equal(result['balance'], 198742000000) + assert_equal(result['tokenid'], tokenid) + + # get token balance for token without pubkey + result = rpc.tokenbalance(tokenid) + assert_success(result) + assert_equal(result['balance'], 198742000000) + assert_equal(result['tokenid'], tokenid) + + # this is not a valid assetid + result = rpc.tokeninfo(self.pubkey) + assert_error(result) + + # check tokeninfo for valid token + result = rpc.tokeninfo(tokenid) + assert_success(result) + assert_equal(result['tokenid'], tokenid) + assert_equal(result['owner'], self.pubkey) + assert_equal(result['name'], "DUKE") + assert_equal(result['supply'], 198742000000) + assert_equal(result['description'], "Duke's custom token") + + # invalid numtokens ask + result = rpc.tokenask("-1", tokenid, "1") + assert_error(result) + + # invalid numtokens ask + result = rpc.tokenask("0", tokenid, "1") + assert_error(result) + + # invalid price ask + result = rpc.tokenask("1", tokenid, "-1") + assert_error(result) + + # invalid price ask + result = rpc.tokenask("1", tokenid, "0") + assert_error(result) + + # invalid tokenid ask + result = rpc.tokenask("100", "deadbeef", "1") + assert_error(result) + + # valid ask + tokenask = rpc.tokenask("100", tokenid, "7.77") + tokenaskhex = tokenask['hex'] + tokenaskid = self.send_and_mine(tokenask['hex'], rpc) + result = rpc.tokenorders() + order = result[0] + assert order, "found order" + + # invalid ask fillunits + result = rpc.tokenfillask(tokenid, tokenaskid, "0") + assert_error(result) + + # invalid ask fillunits + result = rpc.tokenfillask(tokenid, tokenaskid, "-777") + assert_error(result) + + # valid ask fillunits + fillask = rpc.tokenfillask(tokenid, tokenaskid, "777") + result = self.send_and_mine(fillask['hex'], rpc) + txid = result[0] + assert txid, "found txid" + + # should be no token orders + result = rpc.tokenorders() + assert_equal(result, []) + + # checking ask cancellation + testorder = rpc.tokenask("100", tokenid, "7.77") + testorderid = self.send_and_mine(testorder['hex'], rpc) + cancel = rpc.tokencancelask(tokenid, testorderid) + self.send_and_mine(cancel["hex"], rpc) + result = rpc.tokenorders() + assert_equal(result, []) + + # invalid numtokens bid + result = rpc.tokenbid("-1", tokenid, "1") + assert_error(result) + + # invalid numtokens bid + result = rpc.tokenbid("0", tokenid, "1") + assert_error(result) + + # invalid price bid + result = rpc.tokenbid("1", tokenid, "-1") + assert_error(result) + + # invalid price bid + result = rpc.tokenbid("1", tokenid, "0") + assert_error(result) + + # invalid tokenid bid + result = rpc.tokenbid("100", "deadbeef", "1") + assert_error(result) + + tokenbid = rpc.tokenbid("100", tokenid, "10") + tokenbidhex = tokenbid['hex'] + tokenbidid = self.send_and_mine(tokenbid['hex'], rpc) + result = rpc.tokenorders() + order = result[0] + assert order, "found order" + + # invalid bid fillunits + result = rpc.tokenfillbid(tokenid, tokenbidid, "0") + assert_error(result) + + # invalid bid fillunits + result = rpc.tokenfillbid(tokenid, tokenbidid, "-777") + assert_error(result) + + # valid bid fillunits + fillbid = rpc.tokenfillbid(tokenid, tokenbidid, "1000") + result = self.send_and_mine(fillbid['hex'], rpc) + txid = result[0] + assert txid, "found txid" + + # should be no token orders + result = rpc.tokenorders() + assert_equal(result, []) + + # checking bid cancellation + testorder = rpc.tokenbid("100", tokenid, "7.77") + testorderid = self.send_and_mine(testorder['hex'], rpc) + cancel = rpc.tokencancelbid(tokenid, testorderid) + self.send_and_mine(cancel["hex"], rpc) + result = rpc.tokenorders() + assert_equal(result, []) + + # invalid token transfer amount (have to add status to CC code!) + randompubkey = "021a559101e355c907d9c553671044d619769a6e71d624f68bfec7d0afa6bd6a96" + result = rpc.tokentransfer(tokenid,randompubkey,"0") + assert_error(result) + + # invalid token transfer amount (have to add status to CC code!) + result = rpc.tokentransfer(tokenid,randompubkey,"-1") + assert_error(result) + + # valid token transfer + sendtokens = rpc.tokentransfer(tokenid,randompubkey,"1") + self.send_and_mine(sendtokens["hex"], rpc) + result = rpc.tokenbalance(tokenid,randompubkey) + assert_equal(result["balance"], 1) + + def run_test(self): + print("Mining blocks...") + rpc = self.nodes[0] + rpc1 = self.nodes[1] + # utxos from block 1 become mature in block 101 + rpc.generate(101) + self.sync_all() + rpc.getinfo() + rpc1.getinfo() + # this corresponds to -pubkey above + print("Importing privkeys") + rpc.importprivkey(self.privkey) + rpc1.importprivkey(self.privkey1) + self.run_token_tests() + +if __name__ == '__main__': + CryptoconditionsTokenTest().main() diff --git a/qa/rpc-tests/wallet_listreceived.py b/qa/rpc-tests/wallet_listreceived.py index 9ece27772..5d9e583ee 100755 --- a/qa/rpc-tests/wallet_listreceived.py +++ b/qa/rpc-tests/wallet_listreceived.py @@ -28,7 +28,7 @@ class ListReceivedTest (BitcoinTestFramework): self.sync_all() assert_equal(new_height, self.nodes[0].getblockcount()) - def run_test_release(self, release, expected_memo, height): + def run_test_release(self, release, height): self.generate_and_sync(height+1) taddr = self.nodes[1].getnewaddress() zaddr1 = self.nodes[1].z_getnewaddress(release) @@ -61,7 +61,7 @@ class ListReceivedTest (BitcoinTestFramework): # Generate some change by sending part of zaddr1 to zaddr2 zaddr2 = self.nodes[1].z_getnewaddress(release) opid = self.nodes[1].z_sendmany(zaddr1, - [{'address': zaddr2, 'amount': 0.6, 'memo': my_memo}]) + [{'address': zaddr2, 'amount': 0.6}]) txid = wait_and_assert_operationid_status(self.nodes[1], opid) self.sync_all() self.generate_and_sync(height+4) @@ -74,12 +74,12 @@ class ListReceivedTest (BitcoinTestFramework): assert_equal(txid, r[0]['txid']) assert_equal(Decimal('0.4')-fee, r[0]['amount']) assert_true(r[0]['change'], "Note valued at (0.4-fee) should be change") - assert_equal(expected_memo, r[0]['memo']) + assert_equal(no_memo, r[0]['memo']) # The old note still exists (it's immutable), even though it is spent assert_equal(Decimal('1.0'), r[1]['amount']) assert_false(r[1]['change'], "Note valued at 1.0 should not be change") - assert_equal(expected_memo, r[0]['memo']) + assert_equal(my_memo, r[1]['memo']) # zaddr2 should not have change r = self.nodes[1].z_listreceivedbyaddress(zaddr2, 0) @@ -88,11 +88,11 @@ class ListReceivedTest (BitcoinTestFramework): assert_equal(txid, r[0]['txid']) assert_equal(Decimal('0.6'), r[0]['amount']) assert_false(r[0]['change'], "Note valued at 0.6 should not be change") - assert_equal(my_memo, r[0]['memo']) + assert_equal(no_memo, r[0]['memo']) def run_test(self): - self.run_test_release('sprout', no_memo, 200) - self.run_test_release('sapling', no_memo, 204) + self.run_test_release('sprout', 200) + self.run_test_release('sapling', 204) if __name__ == '__main__': ListReceivedTest().main() diff --git a/qa/rpc-tests/wallet_persistence.py b/qa/rpc-tests/wallet_persistence.py index f6c227d2a..581cad473 100755 --- a/qa/rpc-tests/wallet_persistence.py +++ b/qa/rpc-tests/wallet_persistence.py @@ -68,12 +68,22 @@ class WalletPersistenceTest (BitcoinTestFramework): # Verify shielded balance assert_equal(self.nodes[0].z_getbalance(sapling_addr), Decimal('20')) - + + # Verify size of shielded pools + pools = self.nodes[0].getblockchaininfo()['valuePools'] + assert_equal(pools[0]['chainValue'], Decimal('0')) # Sprout + assert_equal(pools[1]['chainValue'], Decimal('20')) # Sapling + # Restart the nodes stop_nodes(self.nodes) wait_bitcoinds() self.setup_network() + # Verify size of shielded pools + pools = self.nodes[0].getblockchaininfo()['valuePools'] + assert_equal(pools[0]['chainValue'], Decimal('0')) # Sprout + assert_equal(pools[1]['chainValue'], Decimal('20')) # Sapling + # Node 0 sends some shielded funds to Node 1 dest_addr = self.nodes[1].z_getnewaddress('sapling') recipients = [] @@ -128,4 +138,4 @@ class WalletPersistenceTest (BitcoinTestFramework): assert_equal(self.nodes[1].z_getbalance(dest_addr), Decimal('16')) if __name__ == '__main__': - WalletPersistenceTest().main() \ No newline at end of file + WalletPersistenceTest().main() diff --git a/src/Makefile.am b/src/Makefile.am index 2f69758d3..874c3a8b4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -261,7 +261,6 @@ BITCOIN_CORE_H = \ wallet/wallet.h \ wallet/wallet_ismine.h \ wallet/walletdb.h \ - veruslaunch.h \ zmq/zmqabstractnotifier.h \ zmq/zmqconfig.h\ zmq/zmqnotificationinterface.h \ @@ -483,7 +482,6 @@ libbitcoin_common_a_SOURCES = \ script/script_error.cpp \ script/sign.cpp \ script/standard.cpp \ - veruslaunch.cpp \ transaction_builder.cpp \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) diff --git a/src/ac/zex b/src/ac/zex new file mode 100755 index 000000000..f4573c093 --- /dev/null +++ b/src/ac/zex @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=ZEX $1 $2 $3 $4 $5 $6 diff --git a/src/assetchains.json b/src/assetchains.json index d26f3a99a..287a88c19 100644 --- a/src/assetchains.json +++ b/src/assetchains.json @@ -118,7 +118,8 @@ }, { "ac_name": "EQL", - "ac_supply": "500000000" + "ac_supply": "500000000", + "ac_ccactivate": "205000" }, { "ac_name": "ZILLA", @@ -186,5 +187,28 @@ "addnode": [ "51.75.124.34" ] + }, + { + "ac_name": "KMDICE", + "ac_supply": "10500000", + "ac_reward": "2500000000", + "ac_halving": "210000", + "ac_cc": "2", + "addressindex": "1", + "spentindex": "1", + "addnode": [ + "144.76.217.232" + ] + }, + { + "ac_name": "ZEX", + "ac_founders": "1", + "ac_reward": "13000000000", + "ac_halving": "525600", + "ac_cc": "2", + "ac_pubkey": "039d4a50cc70d1184e462a22edb3b66385da97cc8059196f8305c184a3e21440af", + "addnode": [ + "5.9.102.210" + ] } ] diff --git a/src/assetchains.old b/src/assetchains.old index 42a1effb6..9164ca316 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -33,7 +33,7 @@ echo $pubkey ./komodod -pubkey=$pubkey -ac_name=PRLPAY -ac_supply=500000000 -addnode=13.250.226.125 & ./komodod -pubkey=$pubkey -ac_name=DSEC -ac_supply=7000000 -addnode=185.148.147.30 & ./komodod -pubkey=$pubkey -ac_name=GLXT -ac_supply=10000000000 -addnode=13.230.224.15 & -./komodod -pubkey=$pubkey -ac_name=EQL -ac_supply=500000000 -addnode=46.101.124.153 & +./komodod -pubkey=$pubkey -ac_name=EQL -ac_supply=500000000 -ac_ccactivate=205000 -addnode=46.101.124.153 & ./komodod -pubkey=$pubkey -ac_name=ZILLA -ac_supply=11000000 -addnode=54.39.23.248 & ./komodod -pubkey=$pubkey -ac_name=RFOX -ac_supply=1000000000 -ac_reward=100000000 -addnode=78.47.196.146 & ~/VerusCoin/src/komodod -pubkey=$pubkey -ac_name=VRSC -ac_algo=verushash -ac_cc=1 -ac_veruspos=50 -ac_supply=0 -ac_eras=3 -ac_reward=0,38400000000,2400000000 -ac_halving=1,43200,1051920 -ac_decay=100000000,0,0 -ac_end=10080,226080,0 -ac_timelockgte=19200000000 -ac_timeunlockfrom=129600 -ac_timeunlockto=1180800 -addnode=185.25.48.236 -addnode=185.64.105.111 & @@ -44,3 +44,4 @@ echo $pubkey ./komodod -pubkey=$pubkey -ac_name=PGT -ac_supply=10000000 -ac_end=1 -addnode=190.114.254.104 & ./komodod -pubkey=$pubkey -ac_name=KMDICE -ac_supply=10500000 -ac_reward=2500000000 -ac_halving=210000 -ac_cc=2 -addressindex=1 -spentindex=1 -addnode=144.76.217.232 & ./komodod -pubkey=$pubkey -ac_name=DION -ac_supply=3900000000 -ac_reward=22260000000 -ac_staked=100 -ac_cc=1 -ac_end=4300000000 -addnode=51.75.124.34 & +./komodod -pubkey=$pubkey -ac_name=ZEX -ac_cc=2 -ac_founders=1 -ac_halving=525600 -ac_reward=13000000000 -ac_pubkey=039d4a50cc70d1184e462a22edb3b66385da97cc8059196f8305c184a3e21440af -addnode=5.9.102.210 & diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index ddfecd47a..83c85e551 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -178,6 +178,7 @@ std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTr void SetCCunspents(std::vector > &unspentOutputs,char *coinaddr); void SetCCtxids(std::vector > &addressIndex,char *coinaddr); int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs); +int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinputs); int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout); // curve25519 and sha256 diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index b94e1372c..b683a7f51 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -431,3 +431,92 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3 #endif return(0); } + + +int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinputs) +{ + int32_t abovei,belowi,ind,vout,i,n = 0,maxutxos=64; 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; + utxos = (struct CC_utxo *)calloc(maxutxos,sizeof(*utxos)); + threshold = total/(maxinputs+1); + if ( maxinputs > maxutxos ) + maxutxos = maxinputs; + sum = 0; + Getscriptaddress(coinaddr,CScript() << Mypubkey() << OP_CHECKSIG); + SetCCunspents(unspentOutputs,coinaddr); + 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; + 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)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 ) + { + for (i=0; itxid = 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,maxutxos); + if ( n >= maxutxos || sum >= total ) + break; + } + } + } + remains = total; + for (i=0; i0; 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); + free(utxos); + return(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); + free(utxos); + return(0); + } + up = &utxos[ind]; + mtx.vin.push_back(CTxIn(up->txid,up->vout,CScript())); + totalinputs += up->nValue; + remains -= up->nValue; + utxos[ind] = 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 ) + break; + } + free(utxos); + if ( totalinputs >= total ) + { + //fprintf(stderr,"return totalinputs %.8f\n",(double)totalinputs/COIN); + return(totalinputs); + } + return(0); +} diff --git a/src/cc/dice.cpp b/src/cc/dice.cpp index 597f2cb38..6faaa1f0c 100644 --- a/src/cc/dice.cpp +++ b/src/cc/dice.cpp @@ -1392,7 +1392,7 @@ std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int6 } if ( scriptPubKey == fundingPubKey ) { - if ( AddNormalinputs(mtx,mypk,amount+2*txfee,60) > 0 ) + if ( AddNormalinputs2(mtx,amount+2*txfee,60) > 0 ) { hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk)); @@ -1505,7 +1505,7 @@ std::string DiceBetFinish(uint8_t &funcid,uint256 &entropyused,int32_t &entropyv { if ( vin0txid == zeroid || vin0vout < 0 ) { - if ( AddNormalinputs(mtx,mypk,2*txfee,3) == 0 ) // must be a single vin!! + if ( AddNormalinputs2(mtx,2*txfee,3) == 0 ) // must be a single vin!! { CCerror = "no txfee inputs for win/lose"; fprintf(stderr,"%s\n", CCerror.c_str() ); @@ -1652,22 +1652,32 @@ static uint256 dealer0_fundingtxid; void *dealer0_loop(void *_arg) { char *planstr = (char *)_arg; - CTransaction tx; CPubKey mypk,dicepk; uint64_t entropyval; uint256 entropytxid; int32_t entropytxs,i,n,num; CScript fundingPubKey; struct CCcontract_info *cp,C; char coinaddr[64]; std::string res; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t refsbits,txfee = 10000; + CTransaction tx,*entropytxs,entropytx; CPubKey mypk,dicepk; uint64_t entropyval; uint256 hashBlock,entropytxid,txid; int32_t height,lastht,numentropytxs,i,n,m,num; CScript fundingPubKey; struct CCcontract_info *cp,C; char coinaddr[64]; std::string res; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t refsbits,txfee = 10000; if ( (cp= Diceinit(fundingPubKey,dealer0_fundingtxid,&C,planstr,txfee,mypk,dicepk,refsbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) { fprintf(stderr,"error initializing dealer0_loop\n"); exit(-1); } fprintf(stderr,"dealer0 node running\n"); + height = lastht = 0; + entropytxs = (CTransaction *)calloc(sizeof(*entropytxs),DICE_MINUTXOS); while ( 1 ) { - DicePlanFunds(entropyval,entropytxid,refsbits,cp,dicepk,dealer0_fundingtxid,entropytxs,false); - if ( entropytxs < DICE_MINUTXOS ) + while ( KOMODO_INSYNC == 0 || (height= KOMODO_INSYNC) == lastht ) { - n = sqrt(DICE_MINUTXOS - entropytxs) + 10; - for (i=0; i 10 ) + // n = 10; + for (i=m=0; i 64 && is_hexstr((char *)res.c_str(),0) > 64 ) { if ( DecodeHexTx(tx,res) != 0 ) @@ -1675,12 +1685,37 @@ void *dealer0_loop(void *_arg) LOCK(cs_main); if ( myAddtomempool(tx) != 0 ) { - fprintf(stderr,"ENTROPY %s: %d of %d, %d\n",tx.GetHash().GetHex().c_str(),i,n,DICE_MINUTXOS - entropytxs); + fprintf(stderr,"ENTROPY %s: %d of %d, %d\n",tx.GetHash().GetHex().c_str(),i,n,DICE_MINUTXOS - numentropytxs); RelayTransaction(tx); + entropytxs[m++] = tx; } else break; } else break; } else break; } + for (i=0; inHeight < 6400 && !strcmp(ASSETCHAINS_SYMBOL, "VRSC")) + /*if (coins->nHeight < 6400 && !strcmp(ASSETCHAINS_SYMBOL, "VRSC")) { std::string hc = input.prevout.hash.ToString(); if (LaunchMap().lmap.count(hc)) @@ -568,7 +568,7 @@ const CScript &CCoinsViewCache::GetSpendFor(const CCoins *coins, const CTxIn& in return txData.scriptPubKey; } } - } + }*/ return coins->vout[input.prevout.n].scriptPubKey; } diff --git a/src/coins.h b/src/coins.h index f35af02f0..39d1e22fc 100644 --- a/src/coins.h +++ b/src/coins.h @@ -24,7 +24,7 @@ #include #include #include "zcash/IncrementalMerkleTree.hpp" -#include "veruslaunch.h" +//#include "veruslaunch.h" /** * Pruned version of CTransaction: only retains metadata and unspent transaction outputs @@ -456,7 +456,7 @@ class CTransactionExceptionData CTransactionExceptionData() : scriptPubKey(), voutMask() {} }; -class CLaunchMap +/*class CLaunchMap { public: std::unordered_map lmap; @@ -477,7 +477,7 @@ class CLaunchMap } } }; -static CLaunchMap launchMap = CLaunchMap(); +static CLaunchMap launchMap = CLaunchMap();*/ /** CCoinsView that adds a memory cache for transactions to another CCoinsView */ class CCoinsViewCache : public CCoinsViewBacked @@ -507,7 +507,7 @@ public: ~CCoinsViewCache(); // Standard CCoinsView methods - static CLaunchMap &LaunchMap() { return launchMap; } + //static CLaunchMap &LaunchMap() { return launchMap; } bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; bool GetNullifier(const uint256 &nullifier, ShieldedType type) const; diff --git a/src/fiat/zex b/src/fiat/zex new file mode 100755 index 000000000..f4573c093 --- /dev/null +++ b/src/fiat/zex @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=ZEX $1 $2 $3 $4 $5 $6 diff --git a/src/gtest/test_checkblock.cpp b/src/gtest/test_checkblock.cpp index 01f098004..807191b5d 100644 --- a/src/gtest/test_checkblock.cpp +++ b/src/gtest/test_checkblock.cpp @@ -291,7 +291,7 @@ TEST_F(ContextualCheckBlockTest, BlockOverwinterRulesRejectOtherTx) { { SCOPED_TRACE("BlockOverwinterRulesRejectSaplingTx"); - ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-overwinter-tx-version-group-id"); + ExpectInvalidBlockFromTx(CTransaction(mtx), 0, "bad-overwinter-tx-version-group-id"); } } @@ -319,6 +319,6 @@ TEST_F(ContextualCheckBlockTest, BlockSaplingRulesRejectOtherTx) { { SCOPED_TRACE("BlockSaplingRulesRejectOverwinterTx"); - ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-sapling-tx-version-group-id"); + ExpectInvalidBlockFromTx(CTransaction(mtx), 0, "bad-sapling-tx-version-group-id"); } } diff --git a/src/init.cpp b/src/init.cpp index f3d728933..a87eb6170 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -844,7 +844,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) const CChainParams& chainparams = Params(); // Set this early so that experimental features are correctly enabled/disabled - fExperimentalMode = GetBoolArg("-experimentalfeatures", false); + fExperimentalMode = GetBoolArg("-experimentalfeatures", true); // Fail early if user has set experimental options without the global flag if (!fExperimentalMode) { diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 1a0fad409..c99a5f1a6 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -1771,12 +1771,12 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) { if ( (is_PoSblock= komodo_is_PoSblock(slowflag,height,pblock,bnTarget,bhash)) == 0 ) { + if ( slowflag == 0 ) // need all past 100 blocks to calculate PoW target + return(0); if ( ASSETCHAINS_STAKED == 100 && height > 100 ) // only PoS allowed! POSTEST64 return(-1); else { - if ( slowflag == 0 ) // need all past 100 blocks to calculate PoW target - return(0); if ( slowflag != 0 ) bnTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED); if ( bhash > bnTarget ) @@ -1796,6 +1796,8 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) fprintf(stderr,"unexpected negative is_PoSblock.%d\n",is_PoSblock); return(-1); } + else if ( ASSETCHAINS_STAKED != 0 ) + failed = 0; } if ( failed == 0 && ASSETCHAINS_COMMISSION != 0 ) //ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 ) { @@ -1824,12 +1826,28 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) return(-1); } } - //fprintf(stderr,"komodo_checkPOW possible.%d slowflag.%d ht.%d notaryid.%d failed.%d\n",possible,slowflag,height,notaryid,failed); +//fprintf(stderr,"komodo_checkPOW possible.%d slowflag.%d ht.%d notaryid.%d failed.%d\n",possible,slowflag,height,notaryid,failed); if ( failed != 0 && possible == 0 && notaryid < 0 ) return(-1); else return(0); } +int32_t komodo_acpublic(uint32_t tiptime) +{ + int32_t acpublic = ASSETCHAINS_PUBLIC; CBlockIndex *pindex; + if ( acpublic == 0 ) + { + if ( tiptime == 0 ) + { + if ( (pindex= chainActive.LastTip()) != 0 ) + tiptime = pindex->nTime; + } + if ( (ASSETCHAINS_SYMBOL[0] == 0 || strcmp(ASSETCHAINS_SYMBOL,"ZEX") == 0) && tiptime >= KOMODO_SAPLING_DEADLINE ) + acpublic = 1; + } + return(acpublic); +} + int64_t komodo_newcoins(int64_t *zfundsp,int32_t nHeight,CBlock *pblock) { CTxDestination address; int32_t i,j,m,n,vout; uint8_t *script; uint256 txid,hashBlock; int64_t zfunds=0,vinsum=0,voutsum=0; diff --git a/src/main.cpp b/src/main.cpp index de9fe34c3..6c33feb08 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -77,7 +77,7 @@ static int64_t nTimeBestReceived = 0; CWaitableCriticalSection csBestBlock; CConditionVariable cvBlockChange; int nScriptCheckThreads = 0; -bool fExperimentalMode = false; +bool fExperimentalMode = true; bool fImporting = false; bool fReindex = false; bool fTxIndex = false; @@ -1034,9 +1034,12 @@ bool ContextualCheckTransaction( } // Reject transactions with non-Sapling version group ID - if (tx.fOverwintered && tx.nVersionGroupId != SAPLING_VERSION_GROUP_ID) { - return state.DoS(dosLevel, error("CheckTransaction(): invalid Sapling tx version"), - REJECT_INVALID, "bad-sapling-tx-version-group-id"); + if (tx.fOverwintered && tx.nVersionGroupId != SAPLING_VERSION_GROUP_ID) + { + //return state.DoS(dosLevel, error("CheckTransaction(): invalid Sapling tx version"),REJECT_INVALID, "bad-sapling-tx-version-group-id"); + return state.DoS(isInitBlockDownload() ? 0 : dosLevel, + error("CheckTransaction(): invalid Sapling tx version"), + REJECT_INVALID, "bad-sapling-tx-version-group-id"); } // Reject transactions with invalid version @@ -1058,9 +1061,12 @@ bool ContextualCheckTransaction( } // Reject transactions with non-Overwinter version group ID - if (tx.fOverwintered && tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID) { - return state.DoS(dosLevel, error("CheckTransaction(): invalid Overwinter tx version"), - REJECT_INVALID, "bad-overwinter-tx-version-group-id"); + if (tx.fOverwintered && tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID) + { + //return state.DoS(dosLevel, error("CheckTransaction(): invalid Overwinter tx version"),REJECT_INVALID, "bad-overwinter-tx-version-group-id"); + return state.DoS(isInitBlockDownload() ? 0 : dosLevel, + error("CheckTransaction(): invalid Overwinter tx version"), + REJECT_INVALID, "bad-overwinter-tx-version-group-id"); } // Reject transactions with invalid version @@ -1253,10 +1259,12 @@ int32_t komodo_isnotaryvout(char *coinaddr) // from ac_private chains only return(0); } +int32_t komodo_acpublic(uint32_t tiptime); + bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context - int32_t invalid_private_taddr=0,z_z=0,z_t=0,t_z=0,acpublic = ASSETCHAINS_PUBLIC; + int32_t invalid_private_taddr=0,z_z=0,z_t=0,t_z=0,acpublic = komodo_acpublic(tiptime); /** * Previously: * 1. The consensus rule below was: @@ -1359,8 +1367,6 @@ bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransactio return state.DoS(100, error("CheckTransaction(): tx.valueBalance has no sources or sinks"), REJECT_INVALID, "bad-txns-valuebalance-nonzero"); } - if ( (ASSETCHAINS_SYMBOL[0] == 0 || strcmp(ASSETCHAINS_SYMBOL,"ZEX") == 0) && tiptime >= KOMODO_SAPLING_DEADLINE ) - acpublic = 1; if ( acpublic != 0 && (tx.vShieldedSpend.empty() == 0 || tx.vShieldedOutput.empty() == 0) ) { return state.DoS(100, error("CheckTransaction(): this is a public chain, no sapling allowed"), @@ -1725,7 +1731,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (pfMissingInputs) *pfMissingInputs = true; //fprintf(stderr,"missing inputs\n"); - return state.DoS(0, error("AcceptToMemoryPool: tx inputs not found"),REJECT_INVALID, "bad-txns-inputs-missing"); + //return state.DoS(0, error("AcceptToMemoryPool: tx inputs not found"),REJECT_INVALID, "bad-txns-inputs-missing"); + return(false); } } @@ -1979,6 +1986,7 @@ bool myAddtomempool(CTransaction &tx, CValidationState *pstate) bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) { + memset(&hashBlock,0,sizeof(hashBlock)); // need a GetTransaction without lock so the validation code for assets can run without deadlock { //fprintf(stderr,"check mempool\n"); @@ -2022,6 +2030,7 @@ bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlo bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) { CBlockIndex *pindexSlow = NULL; + memset(&hashBlock,0,sizeof(hashBlock)); LOCK(cs_main); diff --git a/src/miner.cpp b/src/miner.cpp index 8fb2b4677..9b0a04aaa 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -463,8 +463,6 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, uint64_t txfees,utxovalue; uint32_t txtime; uint256 utxotxid; int32_t i,siglen,numsigs,utxovout; uint8_t utxosig[128],*ptr; CMutableTransaction txStaked = CreateNewContextualCMutableTransaction(Params().GetConsensus(), stakeHeight); - //if ( blocktime > pindexPrev->GetMedianTimePast()+60 ) - // blocktime = pindexPrev->GetMedianTimePast() + 60; if (ASSETCHAINS_LWMAPOS != 0) { uint32_t nBitsPOS; @@ -479,6 +477,9 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, } else { + blocktime = GetAdjustedTime(); + //if ( blocktime > pindexPrev->GetMedianTimePast()+60 ) + // blocktime = pindexPrev->GetMedianTimePast() + 60; siglen = komodo_staked(txStaked, pblock->nBits, &blocktime, &txtime, &utxotxid, &utxovout, &utxovalue, utxosig); } @@ -736,7 +737,7 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, } else { - if (!isStake) + //if ( !isStake || ASSETCHAINS_STAKED != 0 ) { if (!reservekey.GetReservedKey(pubkey)) { diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 91b158f45..c4eb16e0e 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -136,7 +136,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "z_shieldcoinbase", 3}, { "z_getoperationstatus", 0}, { "z_getoperationresult", 0}, - { "z_importkey", 1 }, { "paxprice", 4 }, { "paxprices", 3 }, { "paxpending", 0 }, diff --git a/src/txdb.cpp b/src/txdb.cpp index 212eda7a0..63e00dcd8 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -670,7 +670,8 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nCachedBranchId = diskindex.nCachedBranchId; pindexNew->nTx = diskindex.nTx; pindexNew->nSproutValue = diskindex.nSproutValue; - + pindexNew->nSaplingValue = diskindex.nSaplingValue; + // Consistency checks auto header = pindexNew->GetBlockHeader(); if (header.GetHash() != pindexNew->GetBlockHash()) diff --git a/src/veruslaunch.cpp b/src/veruslaunch.cpp deleted file mode 100644 index 2268b0200..000000000 --- a/src/veruslaunch.cpp +++ /dev/null @@ -1,1424 +0,0 @@ -// Copyright (c) 2018 The Verus developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include "veruslaunch.h" - -const char *whitelist_address = "RCZyeg6eofQUBKJE5LWuiYkgBPCafheDc8"; - -uint64_t whitelist_masks[WHITELIST_COUNT] = { -0x1, -0x1fff, -0xfbf, -0x1, -0x5f, -0x1, -0x1, -0xfff, -0x7ff, -0x1, -0x1, -0x7ffffff, -0x1ff, -0x3ff, -0x3f, -0x1, -0x7ff, -0x2b, -0x1d, -0x7f, -0xff, -0x3b, -0x3ff, -0xf, -0x1, -0xff, -0x3ff, -0xfff, -0x3ff, -0xffef, -0x3ff, -0x1, -0xffffff, -0x7ff, -0x7fff, -0x7ff, -0x1, -0x1, -0x3f, -0x1, -0x1, -0x7ff, -0xfff, -0x1ff, -0xff, -0x1fff, -0x7ff, -0xfff, -0x7ff, -0x1, -0x3ff, -0x17, -0x3f, -0x1ffff, -0x7ff, -0x3ff, -0x7ff, -0x1f, -0x1f, -0x1fff, -0x1, -0x7ff, -0xff, -0x1ff, -0x7ff, -0x1fff, -0x1fff, -0xff, -0xfff, -0x3ff, -0x7fff, -0x7ff, -0x7ff, -0x1, -0x3ff, -0x1, -0xfff, -0x1ff, -0xfff, -0x1, -0x7df, -0x1fff, -0x1, -0x3ff, -0x1fff, -0x1, -0x1ff, -0x1fff, -0x1fff, -0x1, -0x1ff, -0x1, -0x1, -0x7f, -0x1ff, -0x1ff, -0x1, -0x1f, -0x3ff, -0x5f, -0x1fe, -0x7ff, -0x1, -0xfff, -0x1, -0x7ff, -0x3fff, -0x5fff, -0xff, -0x3f, -0x1fff, -0x1, -0x1, -0x1, -0x1bb, -0x1, -0x7ff, -0x5fffff, -0x1d, -0x1fff, -0x1, -0x3fff, -0x1fff, -0x1, -0x3ffb, -0x1ff, -0x1ff, -0xff, -0x7f, -0x17b, -0x7fff, -0x1, -0x1fef, -0x6, -0x1, -0x7f, -0x1, -0x3ff, -0x1, -0x3ff, -0x3fff, -0xff, -0x1, -0xeff, -0xfff, -0x1, -0xff, -0xff, -0x1fff, -0x1, -0x1fe, -0xbf, -0xfff, -0x1ff, -0xff, -0x1, -0x3ff, -0x1, -0x1ff, -0xfff, -0x7ff, -0x1, -0xff, -0x1fffbff, -0x1f, -0x1c, -0x3ff, -0x3f, -0x1, -0x17f, -0x1, -0x1, -0x5fff, -0x1, -0x1, -0x3ff, -0x7f, -0x3ffff, -0x5d, -0x1, -0x7ff, -0xfff, -0x7fff, -0x1fe, -0x1fff, -0x7f, -0x7ff, -0xfff, -0x1, -0x7f, -0x1, -0xf7f, -0x1, -0x1, -0xff, -0x3fff, -0x1, -0x7f, -0x1, -0x1, -0x1, -0x5ff, -0x6f, -0x1, -0x3f, -0x1fff, -0x7ff, -0x1, -0x1ff, -0x3ff, -0x1, -0xff, -0x3ff, -0x1ff, -0x7f, -0x3ff, -0x1, -0x1, -0x3ff, -0x1ff, -0x1ff, -0x1, -0x6f, -0x3dfdffff, -0x3ff, -0x3fd, -0xfff, -0x3ff, -0x1, -0x1, -0x1, -0x7ff, -0x2fff, -0x1, -0x1, -0x37fff, -0x1ff, -0x1fffffbf, -0x7f, -0x1, -0x1f, -0x1, -0x1, -0x7ff, -0x7ff, -0x3fbf, -0xffd, -0x7ff, -0x17f, -0x1, -0x1, -0x1, -0x3ff, -0x3ff, -0x1, -0x1fff, -0x7fdf, -0x1, -0xfff, -0x1, -0x1f, -0x1, -0x1, -0x1ff, -0x3f, -0x1, -0x1, -0x1, -0x1, -0xff, -0x1, -0x7ffdf, -0x7ff, -0xff, -0x1, -0x1fb, -0x1ff, -0x7c, -0x7f, -0x7ff, -0x1fff, -0x5ff, -0x7d, -0x3ff, -0xffff, -0x1ff, -0x1, -0x1, -0xff, -0xff, -0x3fff, -0x7ff, -0xff, -0x1ffeff, -0xb, -0xfff, -0xffff, -0x3ff, -0x7ff, -0xff, -0x1, -0x1ffffff, -0x1, -0x1, -0xfe, -0xfa, -0xff, -0xfff, -0x1ed, -0x1, -0x1, -0x1ff, -0x7ff, -0xff, -0x7f, -0x3f7, -0xbff, -0x1, -0x1, -0x3fff, -0x7df, -0x1, -0x1, -0x1, -0x7fff, -0x7, -0x7ff, -0x1, -0x1, -0x1, -0x1, -0xb, -0x3ff, -0x1fff, -0x3ff, -0xff, -0x3f, -0x1ffff, -0xfff, -0x1, -0x1, -0xfff, -0x3fff, -0x3ff, -0x1, -0x1ff, -0x1, -0x1, -0x38, -0x3ff, -0x1, -0x1f, -0x1, -0x3ff, -0x7ff, -0x1ff, -0x7ff, -0x3f, -0x1ffffff, -0x3f, -0x2f, -0x1d, -0x1, -0x3ff, -0x7ff, -0x1, -0x1, -0x17f, -0x7fff, -0x1ff, -0x3f, -0x1, -0x1, -0x6f, -0x1, -0x1df, -0x3ff, -0x1, -0x1, -0x1, -0x1fff, -0x1, -0x1, -0x3ffd, -0x1fff, -0xfff, -0x7f, -0x1, -0x1, -0x1, -0x7ff, -0x3f, -0x3ff, -0xff, -0x3ff, -0x1, -0x1fff, -0xff, -0x3f, -0xffff, -0x1f7, -0x7ff, -0xfff, -0x3fff, -0x1, -0xffd, -0x7ff, -0x1, -0x1, -0x1, -0x7ff, -0x3feff, -0x7ff, -0xff, -0x7f, -0x3ff, -0x7f, -0x1, -0x1fff, -0x1, -0x1ff, -0x1, -0x7ff, -0x3f, -0x7ff, -0x7ff, -0x1, -0x7ffffff, -0x3ff, -0x7f, -0x1ff, -0xfff, -0x7ff, -0x1, -0x7f, -0x1ff, -0x3f, -0x7f, -0x5f, -0x1, -0x1fff, -0x3ff, -0x77f, -0x7fffffff, -0xff, -0x7ff, -0xff, -0x3ffdfff, -0x7ff, -0x1, -0xfff, -0x2f, -0x79, -0xfff, -0xf7, -0x1ff, -0x1df, -0x1, -0x1e, -0x1fff, -0x7ff, -0xffff, -0x1ff, -0x3ff, -0x7ff, -0x7ff, -0x7ff, -0x1fff, -0xff, -0x7fff, -0x7ff, -0x1, -0x1, -0xbf, -0x3ff, -0x1fff7ff, -0xfff, -0x1, -0xfff, -0x1, -0xff, -0x3ff, -0x3bf, -0x1, -0x3ff, -0x1ff, -0x1f, -0x1, -0x17b, -0x3ff, -0x1fd, -0x1, -0x1, -0x1, -0x3ff, -0x1fff, -0xfff, -0x1, -0x3ff, -0x3fff, -0x3ff, -0x1, -0xff, -0x3fd, -0x1, -0x1, -0x3ff, -0x7f, -0x1, -0xff, -0x1, -0x1, -0x1, -0x3ff, -0x1fff, -0x1, -0x1, -0x1, -0x7ff, -0x1ff, -0x1, -0x1ef, -0x1, -0x1, -0x1, -0x3fe, -0x1, -0xb, -0x3ff, -0xf, -0x3ff, -0x1bb, -0x1ff, -0x1ff, -0x1, -0x1fd, -0xff, -0x1, -0x3ff, -0x1, -0xfff, -0x1ff, -0xfffff, -0x4f, -0x16f, -0x1ff, -0x1, -0x7fff, -0x7f, -0x7f, -0x1, -0x1ffe, -0x1, -0x1, -0x1, -0x7fff, -0x1, -0x3fff, -0xff, -0x1, -0x7ff, -0x1, -0x1f, -0x3f, -0x1fff, -0xfff, -0x1, -0x1fff, -0x1ff, -0x1, -0xfff, -0x7ff, -0xfff, -0x1, -0xfff, -0x1, -0xfff, -0x1, -0x1, -0x1, -0x1, -0x3fff, -0x3fd, -0x1dff, -0x1, -0x1, -0xf, -0x1d, -0x1, -0x1, -0x1, -0x7f, -0x7ff, -0x1, -0x7ff, -0x1, -0x1f, -0x5ff, -0x1, -0x1, -0x7ff, -0x7f, -0x1df, -0x1, -0x7f, -0x1, -0x1, -0xef, -0x1fbebff, -0x3ff, -0x7ff, -0x3f, -0xfb, -0x1, -0x1, -0x7ff, -0x1ff, -0x1ff, -0xfff, -0x1, -0xff, -0x7f, -0x7f, -0x7ff, -0x1, -0x1, -0x1fff, -0x5, -0x1, -0x1, -0x1f6f, -0x1f, -0x2f, -0x1fff, -0x1, -0x1, -0x3ff, -0xff, -0xfff, -0x1ff, -0xff, -0x7f, -0x1fff, -0x77, -0xfff, -0x7f, -0x1, -0x7f, -0x1ff, -0x1, -0x3b, -0x1f, -0x1, -0x7ff, -0x1, -0x1, -0x1, -0x1, -0x1, -0x17f, -0x1, -0x3ff, -0x1, -0x17f, -0x1, -0x1ff, -0x1, -0x1, -0x3fb, -0x1, -0x7fff, -0x1, -0x1, -0x3fff, -0xfff, -0x7ff, -0xfffff, -0x1, -0x1, -0x1fff, -0x3f, -0xfff, -0x1, -0x37e, -0x3ff, -0x7ff, -0x1ff, -0x1, -0x1df, -0x1, -0x1fbf, -0x3fff, -0x1ffff, -0x1, -0x1ff, -0xff, -0x1, -0x3ff, -0x3ff, -0xffff, -0x1ff, -0x3fff, -0xffd, -0x3ff, -}; - - -const char *whitelist_ids[WHITELIST_COUNT] = { -"002b8879209b8f7d57bd0583a5c2eb36316b9cef74df41d68d920b44519f3024", -"00344e3a597a64613b94f52182ee83c4653c11bad812dd2ff6340f0b53f08459", -"011f023e95c53a7d6a707d26a9867cf15cf2cd737c30001d8bc1b3cf2bdf0edb", -"016b5d58ac29880a6769bf44da913868f9a3aba42bb1c0704923b06a33dcda15", -"016cbcc6d43a6618fff83b640e2d84fc578e946c694182ebad0523e2958b5ffb", -"01ff69289553b84d6a59f6182d5c00b2d17efe24c73a5b05a29e954129c6ccce", -"0275c198543c9c80e5e9d2856389e6de2d422180505925300418b98edc3a0afe", -"0277f1d75d5cfbae4e233b1cb51beec6613977ec8f58ab0c4afa994c8c47a2e6", -"0290c7e50a1449c3cbb6e13e38f866a280288768618acb6cd28c0671983670b8", -"02c80c0e2ef82e8665b15e473378587f17c0e3c249c3981523a5c82a94ebd353", -"02ea58496bf645e82bc3c5bed0074c1eb738e499b76e77d45e1b364e58bd8b76", -"038d31196a2345a613cf50b279bdddde4c1f54af3b0427bef9fad2e592932685", -"0484da59a0de43f198e06196f77296df69226a8cdf77184e884ec167c3532cef", -"056aa35cf3605975ebd975dba51192338b0651031cb1a032e690683e0942a83b", -"05eddc863757ab3a0ad92dc10d08bc68d52af7323c5f2f27e87b5b6a88af96d5", -"06fa4a0211eafc2b442723dee8ebcc4067b8c2a8be403b95224d73692b331203", -"077ac942ae3e4abff86cf6d7816d6a2451ca4173f1129361cc36455b6bbe6134", -"0816537b3bd2716fece38a5c79f7b5aec716ea53e9fa42b0a061faaa401c61b1", -"087c5be47004dfded4922baec550810b4e3a70294b4b69fb9074e30bd1d9a892", -"0883c592722bcee50890559865932e9e79e7fb87f56bd7c0e8d895078ea869f6", -"09eb001bb9b9107863e4850263f91d59c74477b8d89ca57ca625301e9a3fadc5", -"0a461587f0a3fd534dbfd6b505bb30da5b285a7b456bad457a0090799624e6ae", -"0ab8ec4b0f3dd37bfc2ae70bba07526a76339cc45eac861ad36b38b7512699a6", -"0af72ab4c895af9464162ab330c7582d694d9ae27ddf042959edd726ca1b7b14", -"0b1b7dad08224bd5dff990f5d04668f4ee216d637a3e4c49751ec997e3762502", -"0be8634afca5648c45495eb6bb9c853e226ca258e7fb9898916dd64a1a822f04", -"0c033aa83bc0da89d4dab7ba96c8a0ad77114bb49b47da011f84883eed301af5", -"0c4ec5a2fd3c7b1f6b544ee89b7f8dd9bcf867485ee0c59bd587f33968833cf8", -"0c700419bcb1787ba200eb28da5de1512f58906c0e358abfc20157282342962b", -"0cbe8330bf17b9d9b7a7b5d86e13c2b4e4145c8cbd6a602d04e71fa860922391", -"0ddb51dd66a1f556e66c904e2e3bf7b7c6b057f316b33681fade82e703411334", -"0e3d414d58f55e91fda7347bdd343547aa7fc169277e5570a24c76db515ed37b", -"0f057e3315a1b473231cbfa452ba55b408fd9da587fd24bea625e5b707745ea5", -"0f25601c3b6f2da84511262c7491e73ea4ae32b31f9041f6a82918c5c008b6cb", -"0f82140db1c65669331edac3da36b05188178c0a70a79139ea553380f1e87fb1", -"0fb23e7be4dbb0d7b410b74351a9029682bf0021a6d52243c78275cf1f14cab5", -"0fe7db2eae67ef22a0d8cdfff494b160598e9fb85fde16beede19503b88d8f44", -"0fe8709b0b02d2ba1e6bb45188e68306e38286c0a4764a7efa50e0415f897e14", -"1101bcf5fdc44d1e51f8e4d9aa01a86c27d5f790926b5df00cdd049ead4d7fa9", -"11182dd29ee6a40d5aa0f1cc982ace6c2877c47072d551db853115e672448fca", -"11bf1e8898837cec381d6fcb8736b5f9507325bd6fb2c01fc0ecf374b07e0fba", -"120eca38ff7909168915af93cc15c6516dfa942da166744fea26cd2f149c32de", -"1211916bba3c8bea6e71972fca03102ee3555b2da4232f127dbcc4c5017b0825", -"1229657075f7503357e389260c3b74969d37c3be381d435d40078c653ea9f3a7", -"127e36a87665d002c7a92b98ab6fce9a3daf701d472f065d76ecf92f501943e2", -"12922ccdb8a360590cf32546575243665da5d012ae586c331bc353f58923e771", -"12a106f2e625b5a08f9dc2102f8eea180e8975974b41115702dbaf6cfbecc723", -"12ab340a91b6c3e7bf02e8babfe0d1e980eb6503333adf4d41818b7c22d34905", -"12eb374b2a88f53c0d29f7bc612e298eb436f18f5995294722e706a9142bc36d", -"1345450fb4c0378e7980d0a740d0b7144e77ce6ed53577d91b259fc3a42c73f2", -"135e66bf2414e41f735a2dfa8ec35bdeb019f7d2c7c5bf70dec056cc757e0a61", -"1430eda829af2535d4f5dcb2d213fd12b4e1ac207d092f55eb6ad241256dff33", -"149b9ae40c8e4b83693f69e79b1b27398f44bd35c434cefc481a2a61e6f23367", -"14a3ca96180f530efaba5ff3cd9640c2ff1deaadd04c43c7d2c31134eb179bdf", -"14be2ac61b6f7d59c0f5ab9fcd8c243b74c8e410b246c5d731cf5f3e0dec6d3a", -"14dd1f04d04fce2233522d6185203371d6723c10e078f73300caecea522f08cd", -"14fb7b06706c392728c684248e146bf73a89420584c6e5800481867164661425", -"15304ae2ff1375b2c0a45cb031f81509d3dd1f9a1bf8451dba43a997f95f8c1e", -"1547b51f12ee567819ecc3e1550d2a378fd4c98868db51064faf591754b3454f", -"1565d746c24dbe5ce8ee76c7fadff23bb96093a2714a2e43019c40a7a14f27fe", -"15dd7a70f258291b58f6afa56cf89f1b1ab537578110e4ddf770e67abba12f5e", -"16ec17c1ada1da7a2678d208f287b57cd34de3701a28765fa30b1eca1536d6ff", -"171ec86aba7306b3825044f1d2b2f8d3bb642a7f76cd50e048994835f0dbb4f7", -"172d6a1a3f8cf8ff7effdbdec4b91f42bb93e234f7e0c3b4c99105a3b8dd6e12", -"17648a8ada3162bfd6c61dab3a68b6d6a6c416050b5e72186a5f4a0fec1f675e", -"1816c505bf671ff884c1a33758c0617ba5be6c10a64a09b624fdf6052ee9df72", -"1848038400d70b3903d00494b74e699719cc8d7136a23ce1c01678caf9367853", -"18557e2f0976a61d25b00d88306835f19fc98e7b4060c54b1f7fd8af58cb8fe6", -"189d98b261c075e5b8dec117760ddf2f98f13ed7aa135926793cfe01ad26e364", -"18e55215c5bf0bc69dfe1130e61f3cd1b96cde286b3caa873455d3f087dbb549", -"18ed4402e50441c0efe1f0652d0decd8955a3e531fd4026b6b49c40d8377f7b7", -"19165ef3862a34394c8a19acfd6d04817f29c4d15b8c43d9d85efa6a4f012a45", -"1a48faaa69b59d4468eeb12dd348635451ea32f15360676a64a37d62475632cc", -"1a512df86481b517e070482b303c7bd44e2d70f69ee5ce65f0f011e9a7ee6ff2", -"1aa54d1c1eee3cee30416be5544d54f7966ac521b7be80d9c98453755425a825", -"1c2e03b2ed08d3ee273fd20e68174a21485a964a43391c2fc3ec9d119747af29", -"1ccdb80b14a6e34f2280a740247a7ae064511f07c25def71d95f709df20bedac", -"1ccf44427bebd855d79a9361844eb90bcfbcf6f00ab2a2dc1c9aec3bdf8c246d", -"1cf60e92909559165e48f0138cfafe59f480d205554e46df1dcfc4edff200fa4", -"1ddbd47203e47a921784c9c4c568528c3b1b82620d7b550156191fa6ad0cb067", -"1dffb30da463fb43f0fef54f86239ba15fe54d290a7019ce61ebd516de2fab6f", -"202909962c024a6ae2000d4e28f3f6c5e2a2e84b1e23d099a4908580d3962d6e", -"20de587f30726bf08f8c804f165ddea52c01c886bc180d203268e358f87f6484", -"21a8d79d4cb3950c16c76ca1b0c1f3ee45dbd2989f0b9f4d13aee9afbafc3073", -"21def17ab16d3290a33bd7bffdfa940444228804921cf50fa05a668ba1b7f832", -"224996985df9fc62083c27679a6df3f09087168422b0428511f9e9cc082dd408", -"225e665c881fc73df9c1eeabc791dd665784cfd2c06707c22eb47e9f06659e56", -"22740025cb024d92ebbf48ad49629a51cab16f10840cdcc8a937ca14e9c14dd0", -"23f285057e832019f6afc5ec2669461adfaedd1dce145322423c6e19036dd833", -"241710960dc4f4941ee3090154facf70522ff50e036311778305a82635669e84", -"241b848f9419448778f9a938f73ba0cf968670fed00331f3a93721d41b26099c", -"24a3e671772418a897e32c070d04a76387cb7671ee97a21a205885eba1f24587", -"24b9b8737ea244916d18447fad586a7423ff13f96b910e58f18d5e63fbbd9fb3", -"25876e3596b7657dcba55dab807eb018f397fc638583fe6ff56af3373a1906e4", -"2635848f52451f402cf2fba66d330eca44bea0f59830f5cd2fcd0193e39a0aca", -"26a78c19dcea41f1542bd5a154bc5c4aff33ed0520e34483b438633d8f13419a", -"273c4957a1f32a95b305748708828286fbb4c19168b7c5f509a0079949a6a61e", -"274607b0c7e368c335e8109797dbe0cb9a21504ca13dfb06347e1e9f459e3efb", -"274c9913f932cffd8337b7a33e25d838303ef8437c321d964b0e79557840e207", -"2761c2790b9320b6ee3bcb92490e76f8992d2991fcc18e74376cc16c34b32e48", -"27a66b01585f5534badc5e093b11ce9575bb8441c5915866cc504148e4db33cc", -"27cfd5a76495ac5d80a5a98a23790d683c0a1a3341a1217a124d259e216d2065", -"28706d3d00fb09dd0826894d439862a685b1812c403b3971c1c954096ebf0feb", -"2875b37c502b010e5ca5707e5b57d69f5b43b149da56242f63032a63fe0a422b", -"2885423a3b21150833cee9262f1b63e4808fee60c55ad1397d0947a5a072b568", -"28a9995c7b2b0d4e06f1c38f814e68d5f750653c7537735cc496c06b571f38dc", -"28c5309eba71024fee274b81b80d324f7ba6fe49b936c44748bf3e8746949dba", -"28e0f04ab585cc0e008bc64acbcdaf9a3df5b685c24ccf71f893743033d29b19", -"2986d25767030441a966b950fe6bf4216c24faa84c76f523514e69eb9d73ec34", -"2a3ca2ba85f27005c06b0f2b9ef8701d4370aa28bc34b0a76f4839d41924ba14", -"2ae0117cd983f09ad379c1364578e53b6ba2f3b03698c866cc7b9848d67a0b53", -"2b0d21da9d0dd74b6d4ed992cae39794e894d83412c7f43d6d31836d8e1b7d18", -"2b4f7a0b1061d17c6439f401b57eb4a4714b54c3931d8b1940af831c64416c8e", -"2bf36600d99ecf4d04d97b0d6c097aecc73c94d6e7e9a9001c117a8d08e625a4", -"2bfabbce5b1a897b22a75ed730015eb33499abc5e96f22f9349171b8d02957dd", -"2d1782b21aef0877213492483339dd08f874342cd39d19df64de160a799ab4af", -"2dda980b9224698a05649f6cd732e9c20977b02f09eb53d15948a2526e836f56", -"2e207e917e87dd8b39f24255dbcb823f12de3b549b000e3f75e0b966ebbf2b83", -"2e7d3950a49f067a9cb4948b6d2fab3e6fcab33002d5118d7014408283d42cbd", -"2e835b4c84a64bfa706ac9e077a08eb6c2162370799ca90ff7d95290feb353ff", -"2ea286dbfbcac3fb669f83c1f02d78737556e2fba41e03cfb1ae01f01ce2fd59", -"2eb62ae8f3ac85a7d8928a306f28e617fc405d07e2eca4688045156440017cc3", -"2ebd55c901bb94ac0bd165af01c79581e48f5d818691114cd4b1333502ab2b35", -"2f5692f0f88dfb3f3dbb0f4aa4979e55f68f054c092f94947847d4629b047848", -"2fc347f2419255a182421dae97f58b3de020612947692a5b41d15ebae59e8ec1", -"3054bf7cf90248e1991426516b3626544a0c1698905020b1f51641bb37b8b15f", -"310319d2c9782aa1677eb95f1e3b71922cf463da8df12e85de51d9c424793e6e", -"325051a1eb2a4ee5b1fdff9f7b5df042c7cab956df80450a7f6d5518c6947890", -"326dc1fb8b73bd48ed5609020492f339140fd54aef7852370a520afa8f3fee1c", -"32779939ba8ced9ce9f0ed71f40dd07c735d5ad61ce4822c6ff653c78ce54aa9", -"32da5a9ba4ae50c9388f17152d41905fed13f6b45f0bf9fef98916128bb48323", -"33653c6a3f6c6e72843c812eb11a0118399fc059ada8c2fe05a01690ba28d4cb", -"33ec7a535be679d54563c2f4205bd80580b6ad09048e703e2d0fe76faf9c04d8", -"3417683f6d80a564b8ce41a509cfc61b3bf927fc2d412cc48f7c559a632aa20b", -"3455da8af23762bb9c12a718d2df2dc3a6b08222f5a1d837f298530098a12739", -"34703485206f3f629cfab6bf95ddcdfc6db509605d4753d2aa405216db5ccb87", -"34ca230f687e0de838bec71e9f8f07fa30d31e9da3e7c6a36ac3db7370bcf770", -"34e46667cb9070f952c1931705f0213a3af34617e8779f1552cd08d42b6abbac", -"34f2ebd727400ddd81aafa28454490ae7e6fd3ba7f5d01ef4f56c0136a43391a", -"350a3096f337e19a4c8462c18a6aed63bbb6079eff28b9cc32ffe407471dfedc", -"358e2dd41e9ca21b7a4c06e271d314bd2c612888896d39c91862d3d3f80b146a", -"35e1bfe2247a20df0f3504883a65e36ca13d7421893f402201209cd5c4af6c91", -"35f7854642cc0ca565d86f543f5c1995d951479a2877836167b9a05dfbb01453", -"36a89621c47190b0fc03912d62b047c680710ed3a3b9ab7591fd9d4ccf1783fd", -"37028127c214dfe94ec70054bebe79d300fd68e6cdb48d7ee3a8a6e7efc2387f", -"373233f53d7d6fc848a3d1d44d9c94a76a0a9d5cb3a172e00e77e08445f40318", -"379ddfc9cd654c76438e7a98ecfc06466cdce4be178e3ecf1f825a7b8c19c684", -"37c8673f4ebc328693a76dc21017cee31699d480462bd81d9229ee018af56df8", -"38d17a3fd47f8ebf9f6f2b89f0a80af8e252219792c89c40c2923b49855f6dba", -"38e314049e2c927fec6e8dee40b35766c993192b26e87861ede1bcc19201dab9", -"390352708c8ac66b247594f9087cc5a334d63bc96e4c76f6c96169d2a4a893ae", -"3947cc691769d0a07513922d717eb64452a89b679fa784878b2c5183a034c12b", -"3975767f9b45b49cd5693116b5830b0576c20516b50244e2bbf2e13f4fe4284e", -"39a9981213f180db60a44e68a9a7a0ae3feacdc9c4b00039dbfd8718f3091823", -"39f2f86b06fda8c40cbc3b787dd00f21684698431eff462cb392d17f15fc628b", -"3ada7a44cd85dac024517a5f8be890d10ab9c8c1c408ceaa1a6695dc793cf0ba", -"3ae37dd8fd9e8f9adb55abfc13765a1d0c4b95db2775e4b70c0d1c4b9246c13b", -"3ae9e37f0d98637ab40c4f221e2653098eee07ffeafdc612fc308e3057478e3f", -"3b81e3a208a22283e7af9432eb52b262d3cd79ff7c58a00288b3aa6ff5b3ff01", -"3b85c87f57308b00155a27ea8551f5cc1b1df95f866c632e62d801522e937a75", -"3b8fd271479ae55236a0cebff624367d3328aa946e1ebc936f5f2e6975107e35", -"3b9933e8814e274a68f8bd03a7403dddedc9d541541082a892ce70b0c81f2f27", -"3ba5a78d5dfa4c70d547e590b32efa5e99ef8fec2285ff267443b63051ad7be8", -"3bc180238397214448f8119a3a86e41a74c940da99a3af22bb955f463b6ea279", -"3c0e34bca907d376439e4f281439ec78587fc09aa92f13836caa374040589994", -"3c3ef3b2874b8d0be8273e57458616e08fac9c4d446b9eaec8a126bd3a43bc5e", -"3c440ea26ad38cb91895e06ef27ff78c3e3a869d70cd29dafafed465911f0822", -"3ce9e5924fcc25a1569d5cded8e4c78c1edea0a4192e1a05cd3206e37b96d29b", -"3d842670e7e521a6271490056b0389d94db76738cb056deea89e9d19d1f27ddd", -"3d9e80491a81a3b8926c5769d8723a13ac9b408572afb14130cecb50172637e1", -"3dcc7a9ff3383e90bc2d29f4ff6cc03a3b2cb11f614d8faec5b7949d8157cad6", -"3df2e641a404394816e4c069c11720da0a5573724db1499c706ac649d58deb69", -"3e037df06c2f24a9faab67e416cc1b8a5fc185e09bce0bd7c064e08571c030fc", -"3e1e2e18553e703c9a4696d4341f1e7ffe973537af0665c6c61fec6a0d714e1b", -"3e6360c2d6cabe18067e31f77cab4dddce151d408d6fe03a966c774cd11d9045", -"3e8649b328904cb4fad71e4e98953567d405323b4a92ab371a396c4bce0bcb49", -"3e9688b9ca4f83f14df8d5ee1612f336f1534ab466b9cf9ace0d617673113d16", -"3fac933733a58054e968e1d07ffe88dc4a21e3b5f11a73035c5ab112ad420440", -"3ff079ec5f4df2fb44d559ed6b80e6437759baa6ed6a6450dc25b18e29cc108c", -"4028392b3b24f1e6f92588f4aee2aa82d0bbe97d3986bd92970e01a532f002b6", -"41bc2a5ab26754978ddac4f507bf9b7955a233b49fccb7db18fa46903c6b0e6e", -"41beb053cf1e516033316a69ab883584cd42546d1d7613181cf3d948eeaf1e6a", -"4226d7e110ac69cf25c6826fd527c1f2bcd2249cbbfbd5560cffbcc79c9666f4", -"4228d3ce33670ea4708a5a33169db4f972ea218a4f70a2f27dfb704638ccc3ec", -"429497d2ec741a49e76a41b80ab61cfbc2255075f271fae0374294c1907448dd", -"4349b963a367f3c2b8458f07ace2bf918b83737368997be7c1de2e718a85f6b2", -"43b75cd385f801eb9957c11d3e320c2a55a0a017be946d293826ce037b02c8dd", -"44f8351eba509fba2936f43daf4b1880b36200752ae2191a69d7ce560271ff95", -"4552a9594f84966bfd05aea07e119ec3c32664cebe47758e64179269ad4d0cf0", -"458ccf7d7f87f61e37b845b2e70647b3584f9335ff030fc2417340dddd3072bf", -"477bc2b25761d86f3ebe08605e9dccbcffd31557f4566917778fd5a10bd35ee0", -"478d64b89f6388454cb06037bcb2230901c290256e53084ba9c26549b550cf2a", -"479519211e1a6e6c5061d619fc37e9a4b598a8ce13f3ba6f6e585a7ec9195d29", -"47cf75c5a350ef00f1e6f64c1c7273cca9427c81a305b951f796ca1e238ef9b4", -"480059f38193d30b8dfff7fdf9ae4eb40426e500172b87d31b6fe755008eb8bf", -"480d99d7f2dc154c10d36862fe6f0cf4ccb156fbc32ef68082796bc01d606f51", -"4822e004b7301667308daf9f6249a64eac2341dd29f155d543a7a1de4dd2a2cb", -"4834a117960b361433c78b8c2108c02964c25b3f29d8316280fe39a4daf4bb93", -"4847aded4abfdcbe41afd2285f3e563bb2253e391f350e36d075bce29be40894", -"4855a717c06619e0564e8a93841f4c947ce5cb2cb734ee91d2a61c4f4b6e26d0", -"4886da7e6c76875c52fe5b016a6bd9ce139a69fb86b854c9feda45822c4df0ec", -"48b1383edd12162c40b17cb1c8306d857a1b8c6624847efa41f4408611e93c56", -"48e9c3d10d527d78c0f46e6cc7026ab0e0f12572f0712a0a0f9c2c411fdf89e5", -"490262b2407938dd372901ce409f61f7f0fd6612685bd504feef2d0d9a5cfba1", -"49e61bb61ba7065cbce26186cb817ce1e14d078ba6fc1a73e4fc7765ca67e9b1", -"4a4827a59d8f52b8b122678158543dd87af6a76f4ed13dec1b1dbde8f2ac3e87", -"4a757f61d5d5738e2016f44a23f7c27ef14230116eab63944a6f18cf23b439f3", -"4a7643336ef44d156f33fec3d8574976dd33456840231b192c9b437d6783a614", -"4a877ce748cc4aa00244aff9ef2ddc6fa1f05d53456cbe9bfc7629d465e80614", -"4aa038d06c4a3a4ee1a4074931039be85fa1b09dc7a22aa5eb0a468e2e349779", -"4aa7d05381013e1e15fb1bac88c72b0d40586ccee6eb66e7d8ddaebe95b52729", -"4b8daa8cf2a99255249ebcec1b8049854876781bb64cbb57d75563f8fd17a7be", -"4b99c08c5f7deae5b2679dcd4727f486c4c4eafa7c06ec443f46665dd9714077", -"4bf4f2a32190e7fa5988e061cde858f0a0cddcf777d72e6ca1ae1323e13e20a0", -"4c2cecee161b749dc94e45faff5786a8aeb99130bf92998b0da653bb4bf47201", -"4cd6fdfa6e0d862ca4dacd59e39cba11902c8a850301093d63bda6e5b196d019", -"4d51cb80a623dd3c544590dc07e718b37ca896169ef724b88cd80cb8e03c53f3", -"4d5ffc17f03d6332cfee37ca66bda4535bc9b65bbea9d3532658fd6239b2532a", -"4e1b198038c9314842587578a8ecf87a3823fcda894573977b4c09d2649c4083", -"4e87bc9294ae8eaad10879f52a20c53467c7469f5a2e6aa0fb7fa54d39636037", -"4f9711d5c450b59118af298916333272d6661a081f321999b2918d1798dca807", -"4fb4e962c9b883685bcc51b88cca48245cf0f1615853046ee20a0703f049fd4a", -"5017b23489cc1ee276fc17894098059b6af278b5bfe82031e252de837fa3b81d", -"503325a82128678ca5b2f2d5344b4ceea0c46e039c7b1148faf4ec77a33d0d14", -"50c009b404e665ed88d6ccd02ad74d1f2a08df2e91a6a0770c2537739db34f50", -"50d94a6876e6f1dc2961f1b040dd4d973277ec3024d4ed38646487b1f2fcba4b", -"5136ad29fa4969b52b593b3b866a6111bc1f7706fcb46b7cea6945c97396f03c", -"51489a8bf67163c40b795b974bd572483d833e24affda21a13bda2c1b6b62d1b", -"519d37b614c45451bbda2aaad0e11fb10a48d9671ac207fcda1b63901e08f830", -"51b0264cfdefaccbee50e37e3bd1c227349f10739731ad582d1c0a2a392e9f8f", -"523c80b259cefd1100d9af6e9796a168da2bec293060b8cf795b0354835522be", -"526969254e21e98f5865d1a49a4d2331bc2365bcb9532898542d2fff2ec52831", -"5293695fe3e59994840c6ce0e51d566ac2ce27aa30ea1c29fd055622c8ef361d", -"530056bf9c906617501a63d0f3b24e88ec4893722811b129c1c9f0786b0c3444", -"5352088de1900c6167435557913792d61a01b007672d532e2c1d8b81934804f9", -"53d4fe04241d20820b0e7b04b025f0f08d7ab20dfe3c5ec450e210843993bbcc", -"53f6efa1c4986dd02a09098a457f7b77ff88067ce2afa611b6d28755d5a26359", -"5454df4d91845d51f21f5abf678eac76149c168ae1d530a1ad3df0e08d09a186", -"558c91e53ca5516108dcbe9eaf5d4586cea5779f7e73f1bd423d24ac867d384f", -"563a15b286fee767853c184920a3a7de2e3d4b2d0bb69daecfc0028ca26e88b4", -"565434775883c4e3f3d2ae70118d06d7489295ea23e94085763e6442f39e7ce0", -"56e7e6d629eaad53a67d0946404943275583a5d85d3cf8df7d30c9cefb526243", -"574595beb493bc89726f4ba19931c3c5dd4b501a4cddf505e08a403f74fd44b9", -"5848b7bb75c2c0d4ed864d530dcc7353ca96c2737731c835a7ccdf2d4cdf88cc", -"58c67f1b4d96c08657bdbcb64f83d160127410fe88fd4689c90933fd3cd32b5a", -"58f0cc940ce668aaa67a42d067a72bc86e8ed3786e3c02c6603b18b817ef6333", -"59155574b65f7f65ea1fe1da623605d630ca25718637ea450d43f406eee961d5", -"594c3b1b23f80b6d36080579aa87802219f1d8cb99ded9d30562a69b65987196", -"5986e9209c7b2bc866a4bf0501d5cf151dc02025285ea8948fd827d59aca7ea6", -"598aac86b8c43996b4d52c9692a1efd76434b3dd06c48bfee1a3cdac1a793c7c", -"59ec9e01ca0406df4d5fc051c86a3a98cdeac3f37b9fc41dccda40181dd17bf4", -"59fc4efb4dd38196bf2c4d2acfc2d3ecf93ffe517bab66f87457cf2c235f83a0", -"5a6a0dcd93a0fc5d33492eb29c4fb99d41da6703dd01d55b1e388892d88caae9", -"5a6a4bd63a1746dee6c9642a39d32234dd0aa77482bc99addaaadb619bde481c", -"5b7f2adbb0206f573506799de281517d5a6e98f54c6c7f6dd16ca3aa2c4834e7", -"5c20eace178b069b5268d34db73638f5284f1a2590fb5788b4605aa53e6160a6", -"5c97a19e3c961e41c1f91efdef9e4ba5c9a438a727cd52cc3980e0fd08daf30c", -"5d0c8759acac45d455ef377c33835b25f7d119cc45bbb09a881d92065cf037a9", -"5d36807e1ba3e002f4157abc58617a3823df455a1afa68a6ed7a3e76e664c30c", -"5d4cde1601df7be6387da3abd1b4ffe273546008eaf980fbeacff4070759415a", -"5d7243f93ebc262982e627db5290a85b064896bd895aa9b89a66289824cfbcd5", -"5df6f48ebe8e04b26f1876140d83ffce865863b96a25409027408157f9cdf56c", -"5ef2d9aea3429d074c405c7a453aada7d245535e7ce23c493a3757a65fb144e2", -"5f792208b94fdcb1c6f3069ddc3deaf9e87d2142ccb0b4be312d33791f02b363", -"602f5b9cc40f822e33f0eb618ea058713d5355aaf32c2cdb077d84872f044a27", -"6074c6cd27db8aa77bdb726aa1ad515c3095f91aa842598282412c2568bd550c", -"60a40518911a7944460de1d0a50afaefc26f838a723568dbeae8677194dbf92e", -"60b186745b934b508cb91998cdb685956a37984e585e11c98a52c1a6ec462ee0", -"61419905bf433295099cc5bdfabeeec6bb73baa50d60b16ae7ef24265d519ced", -"6152fc2088b9682e71836ce597d883ae65d2aa9fbb74c1c71e27974d32be8f4d", -"6258f1ca6d50cbeaacb647b6712f25e8176c9ec7adc4d839499bc00466d92788", -"6312adaa3b8f57f6c2638cba9fb5f0f7a6882f4bdc9da8ea839d38ce186aba2e", -"634a1cbd2a462e44a969b24e0d6057e3da732add991f300ecc48b1998cf9027d", -"635df27912914ab17f57f0f45984181eac6702e7a1a8b042c8bf706c4349c2b8", -"63ec0f9533037693161235df2ee500f5e6114be8180dbfe983c2d5f1d69e4a7e", -"65806b058a3a233be934f70caa7a4db3a9237b6bfdc6978cbe199a2ffc58e18c", -"65e0c595de9ce22dfe88806e2bd264b47fcdfc80c1d615beabad9d1e0abe2255", -"65fe4c41234a38abd98c743bb7038f07fa7121346087ec9d2c0aca9d7f70485e", -"6615dc1227fdba5b31d23ad9794981e935ee1365a3d4cee7dfb95a07e2634fde", -"665f530b2dd27eb214317e755aaef82f00ae752f6e3edcdf1b292ca81572a86f", -"66f762d39c6e0343cc17a8eb2f74a0983b809f7831a9c338360c570e4e083bf1", -"67003ac1d40dda58a254e61cbfe29336ed45c241284e1550eb18fba2085b8cfc", -"67073776f3e6225f2fe75d7c5f97b266fe3daa120c3de02bb79b1de2d06b9ab9", -"675312a324637a99e591d13f8fd4e63d1d732e9aece4cf3dd548d46f6eef738f", -"676101560b4e33eebfdea9d949412571b9857ee033635adcf7bde60406420625", -"679221a1d7461baed18eb82dd9dd0c24289b9b5633b94a0fe96cdab068db451d", -"681ddabf06ce27b56b7f31e0da00e9fa963365ed094125166ac942a126048a83", -"6897766bbf89cfa79180e7dc648dd8fbad31f1975104741daa292ac37380a560", -"6898e0cac3e181c2cd60a610de790d0b8628005f4aab5ae4206cb5cf5a9d02c1", -"696535905e9e1b9d7d8feaed59e4e669a81293cb0c871933790527a16f974fde", -"6a389805eb874698b0bd86f9c61df2d1ff537c87711e8932d94e9781438cda86", -"6a43a3b88a4863311f1c18a1e416411a46eaa40a5dd6d27e6f985c52c8738b56", -"6a4d5694df3b834d29987093ee6cab05af1fc367f074614a3fc49d4b2a8ac49d", -"6a73d14a0701a760b20fabc3f45f054109c3f171c3aa0a42259108b57605d37c", -"6b89b3aef34fae1db8aca96fa50a1a7da07663cd49e91c1f05e4fc9d02637de3", -"6bb9681c746797fb3b9f3150f0a5ca1a277861a38cfa3114378f0fd45613bd6a", -"6be095e513faa67ac2fa0a55407a46f59b87a13bfc62f1f6274718d745e43031", -"6bf05da357b0acfc9559629edc7c3d86bde021301e7f5cb01ad99199fbf9a9e1", -"6c113531f6121a114dd9c4ccdaf1ac78e5102029100218829321162674b0ad8b", -"6d8ff6492d08c7930ed8e52f656812edbc3a4b57909ab6285af7f19e323e7956", -"6dea9a2e3662f0e22f4b906f9261b9edca66585f22235cebe1a0e8b00e024abc", -"6e2d5adb7b85cccab827414ed111ca7d4f9e4c59a02181bc6dbcf95042ed57ba", -"6e5a73e056ce319d0b84010d072edbb01ccb72e8dfce15c9cdc1e0a9c678c8d4", -"6e8b450f4434b05aafb6cf53bee937897707b8878b1fddaebf9d7fcf4399f83f", -"6e8b5386cf14ff1387e4843fe5fd049872ecdb8b6586575f25921535de63670e", -"6e93f13c541a0be612884e550145a09f72c618478ad01def905fd34acadebbd5", -"6ea13a13ca4aab6dd49dd0f12822ea0aba7bbb47334c464ea5494c28c6c88370", -"6f3d69277e39f7f4392f7015c9d3eae6d905061d5356dc20a473c77a72abcd8b", -"6f6bccf50229982a2bccc8903927b5d2bc960dbe8044b224cb9b1cd03e922b38", -"6fa3a32e70242da9f18fb4cb874c35fbc7c5eb527c245651a3f7074454e4815c", -"703ddfbbb6c464b7f5718e8aadb800430a720d67f18bd7c7262ec9e692025ec5", -"7058c92f50ca3d4ff03f81014450b77b7bca11c00b65a09a84c940b2c4da363c", -"708715ba463cf2e0824f5e5c204fe29143dd5c6ce8e246d765178190aa3e9c5e", -"70aac25fbb04170b0c853e2010a683e255a89713c6450e4bd5ea3f6715aa6131", -"7219fa6e5d01fb8a8193341d0f5274e0d9835263f69180125138e12ee945d58e", -"7225fa07f1acf861b2928060f63f757b04f7d525474e9e49ce7e3ab70a715e70", -"728957480847a0f2226ae210957a833174819005a6def020c6b284bf708988d3", -"729b061e0749c52102cc4654539d865b7de77c0600b67c9b26a1bc67a157e4d0", -"72a4436566a08b41fb272f486409961496a3967ccea554759691032dda0cb639", -"72ab445beb477f16a3e364ebf318e072a05119f1171875fa1c6ca0c72dad8792", -"7340f2e48c9566b5bbe21d019f8c89d45f3e868efe8aad9a34b3c8aba2495c15", -"7405399346d6d301704a03ec37fef4e3fd70b5011f7ec767046e504e67b981a6", -"740a8638fb4f54a0898041bb3aa1b4e479428db8c524f4cb8b6513137d52efdf", -"741e7c539b542bd4bdc5489b150d238a683aa55726c5ef8770805121763f1e0b", -"742e5486aaee55c085d58786a7766f811e5638eab8d09bd936156ffdd02d5d46", -"743d899743c6eff5afcf30145694b3ce8f5f30e9e4dfcc29e49da407e93a2537", -"7454fb35f15239ff0076217cd4eda49bdc05f71978d284883d9e47b35bfb1261", -"74589fcec630662347c382f43a81c7b547eb542703f8c50d60aadd6cee8e4816", -"747f2d88f1dccd6e5bcb136004ec327a811986946ea58f0b188ad2a80af7a0b5", -"7506d8d9d20775dbdcb8dc26e4faf9f2997f8e73c1f20c4cb6a0b9346827b336", -"75cb420f6d0f16a357c23798ba4cbab5b40bf72eb6c5e40d29d263a2c4c8c466", -"761b7761473df6d07ba455267dc9a6efea8321527fcfb459529c7c5e33bc8a4a", -"76327346e3741359a0df51c2df10103416faa53fef8f02858c9fec01cd52c214", -"771b9f4e19b05683a5d7508776178094d1165c632a93d03860a44c00a51b5eb7", -"7766a8179fccb69bd0caf975c5f7ec2f72ba0040e89186bbf0f3ba69852cdd29", -"77881656a0a89d985b19e00f345cd924b4df021693ae1b56265a9b541299cf12", -"7790d8c118a812da34707d98926bc65b8372708ec4141a467c46eb33ada37701", -"7793ee3ccd1f18463be869a1b367bf28eedfabf670b4e9a9fe05c280d64844db", -"779e3193eb795967fc70bb19a88dbe76c026d350887176eef48f48f0162924d7", -"7914f19448af9e56c2ef16257b1bc424342086f8a08eba39040aba9575d906fd", -"79b33fbedb764375cc8f05a6c34ac1e7f013fdec417a54aae828112f919a20ec", -"79bedfd9d39125c45904e1ad0b43110886c6b7cce6296d9335e6bb92fe58dc4d", -"79f46d6465397d7d496c194d93d16b52bb777bc50743f367e5fc729ee922dbd1", -"7a6350960aa4d6936edf2d4ddb45f01a5bc1a23bcca72670ec2205471789c810", -"7ab8f8182d43c43b6813600cee1838d4c9ce65d8a4da841397781362ef0cb728", -"7bea041b46a72b405ac3ef5f5eac137e169cb813d9aeb9b277483640ee872294", -"7bf3bb12c1828809f237d0de8b97b6719f8137f10079758f8d9a1b32de90f1b3", -"7ce7d496dba75a34f72fbd1372adb8e33e207b2e1daf17ce1e41b9ef2d0786b5", -"7d1f1e63a9792b26ba62ad7ad27177e54441c5980ca194f988f7e93d76942b21", -"7d494fb086b1da8bfc47b76fbe13c53f0bf89a90eaa0b4190125dfe8c7737ad3", -"7d7a19d707c59ec2fb43860a60adffc9c711bd89b685677fd4c4509877b76024", -"7d91bf06bb71d4d4c9059af32b979bac68c58830f4291d0f1fedd5bee8d868be", -"7d963c75f3b7c13eb999f1e3dc25ac6e65c683b3bfb9b4e4fc5a8dab8da232c2", -"7da4d7f478c29db68fe28d9f9be7c21a34b96240804911c668e4307b3a4f5e7f", -"7db45ec092d1e5fee7b95f7659f9bd9aceb7ba3287ece8950c1a650abe3a4526", -"7dc63b0e876295e920e9bfd12504b9f7c4b85f1724285d8ac441e3ac642ed7c3", -"7dfd0b3aa6533d91674dd7f44cdabfddf0a5ff7c232495443eea7a2586f4e87c", -"7dff6d716e664b88a7996a7858e246542722f748a6ebe98fc8193af0c876d817", -"7e02137ce984537710aeb12680203628ff0c0bca644d8ab876183c91c3590dd5", -"7e29da8c0c9b569d52090a8a4e05578e841da444f1de80958537b8b0c57ef0f6", -"7e35b9676f6e143929d1d0a7401492ca6b735759298e86842ec937d6a4f9ed00", -"7e9fca684c167611fa73c96ecd90f3d034c289223ffe436a8bb1712b8487739b", -"7f5090a09888f69ebf4f349e52a81c9db5bf3d3480aeea5e3638f27b50c428f9", -"7faa66c9a74e6ecba1d217316348dd26f77a3955ad2b470e90d1f1ad5d252763", -"801477fbb380aae6f1fd021fd63fb5d22d021d745997cea163497a6dbd82712f", -"806dc47f0ee4c87e1494c13c98a9c00d7f781f3f7d8046ebaff8ae1507236270", -"812db712d834f2a859870437215330401ba050b796996bf25b708ac712f4d9e4", -"813e56e269ce416236e351ab98ae5a8da2c2bc24df632529960eb56f4a5f5d8a", -"815c385909692679bf70524f2cfa7b2a29bb600f8641ce450bef34c2850368d1", -"817f28d5c20042dc761f8079bdb0315a3d77805548de1d619117149dbe887890", -"82357c1f55d2b2622848c30661bda420a1ec4b01c20f96b935c3e4c70442ea03", -"82429b61e99c73fd518b4f6e84cb722a1e5b730acbf92449b5e42420b8795670", -"82779c5415b3ae646e588d990d7976c09b8aff1bf2f7468e93c787dceddf508a", -"82b76958ee4cd9de1227e710dd823da40674d081400f79cc827e82b0095f8ae3", -"82d9e277089773ba75b31db62b10233e3f418c383625a1be835a9e6f2ca0c5d3", -"8305ce6302ee2266292eb1165afacc7b79514f222e1b7898853a0ebccda5d70e", -"833eb9d97bfd71f730b6d699e348dfce9cce39051d67006f016b0ea9010b1b19", -"8351cb9acaea335a365bbc13d975a410c9ab77ae5d6a6605ac46ad74a048e71d", -"845707a7e12d0b72a7be59a1bc65ec0a359ab65cb30bdf1b12724b41be36d30a", -"847fa24a6c46c117b89180d579157b4707806c0f7236b4546843df3b86ab531b", -"85025f89bb4adc7b1af3500780d4a96fecbdf7ec92748beb7081c1524e611d23", -"854cccce38d91927b5ecdf7a698032048873a202f83e2e2d07183b7357918cb5", -"8550a81b4a24e1ade6c00311ac2346e71ae1a9dcf61265f21864e1faf03c1df1", -"85583915baf4fd22bd6b29385d754d6a9850969c5fff6cd788ca61ce719a495b", -"8576310a71ea0e72b0033d198a692a778a3c0ed7b6abfffd54e6c76a7785d3f5", -"85bf013948212ffae055cf280c54c511798f13b62bf94db0a3c5aaf83bb0da93", -"85d5d7d63b97c0e27b473c28287600ad07f7c8107369c7a3e32430f5c011bcf9", -"8642ccdd7b07b8e3adab6b49aed4562c6d04bdad63ec47714eefa1d1d65456d0", -"8666a378b1164d068a8d325d9c11692f9a6a79ae7e2ef2f72521e6965f5fd14c", -"86aedcb0dafc2f7fe88c498b49d1ad48eb0366c43d2edb2603ea8ba6d2896cd8", -"86b019b257f8cccef914b22857ef373b175a4e75852ab7336d112cb234b07409", -"8740932f7c611b1a6a64894370c8df37604c8755781a269542b16b1ac4794f2b", -"882a62089c81afbf5ce58a0112744b8814a5aa8be328c158a6d9bf5d0e676b38", -"895d89b8a94921fb1475ff7986a3d0c4def81a01adc505d5cede7990ad497180", -"8a67c36720d61d9acf46798b1a50658cc1807deb1adf5f9c035582c2250986a4", -"8ab2e117de4268dd6351b6f30b25eee184211018e85c362de95ac6d99261eec4", -"8ba968865d5fe5c745b70a739084c95ed44412f19c305f0476060cf1d2e5d3f5", -"8c18ad65dd3f05e26e26d5bfe816c2ff3941f5af269771401947d1d07144953f", -"8c2678c037fc9435cd3b46b47423c814360dcd3f3ba5d4e8383311132911fc13", -"8c2f7105e141e012d2d776d14d01429db6ec0465494527397a65cafbc7e90647", -"8c4160ba8a7be774eabf6001fe9fd225259e37699978666b963e392e64b335e9", -"8c7b33dcd60c9cdf01a942b78bd019d84262d8f4f7168e6348c2d8f82e57e82a", -"8c938d3a1d658bae7131f2da052c83e82ac0e4ad68f33eea14d79e382cdc0b7b", -"8cf837a1f020b9d1d82035726b7cbbc1924065f969771d7f15d0330d5628ea5f", -"8d12ff2d891621e8ec8b40b30133535d33f2f7c772367b6cf6f3a7ca99585c2f", -"8d1ffba86bfd3e2a0edb6b4e9a04c6fb23bc2912588c1f8f501992e5e94d4256", -"8d3adbe56f63ef4d0abc87235f5f07da9c84cc7b6c07dca1134de3ea0eeb464e", -"8d80d27d779a08bad79445995b40cdfc967f6f4cd1a81ce57b8355dd1273493f", -"8d8dc1d563c585f623f9fec12475a6f7aa060bc473e2cd32185c655850d80abf", -"8da0a770826004c4a37a0d74dc8d294e189bceb4dbcdca52d0011331240986d4", -"8da8c66d56a861c6bb52d6e12d2488e4118b93bfca4b60671fd92c12ed3a34c4", -"8dd466842944c2e88ab2eda955695cc6fb7afba3c0c45539aec63255465ef0f7", -"8eb75a8b97ebe66be4c2bb5f5fb526beae9aad85fd4fca6515681763f09d8645", -"8ec82cb5286c7d4536909e1f79afbefc203b4b64a77dc1fb2b9549c43c5af7a9", -"90d611eee19c011390205e9130276fc88164f369aaa57e2dbd1a1c540fc655b5", -"912c97d13bea7423aed6137729e399d3da81889c4ebfa8af6a3689f10517288e", -"9172db355c6a0bfff9d64be62c508e7b7a7a29d1b97e9c223f29e78e50f214bc", -"91ac43125d842ff86c848f1f4e4e3814ec8d1d5a66af5469a6fcc54e80302afe", -"91e9f3514429ea59f417cc0c18de1fb7e8a6c0b42d8a20b19b67d0cd922f2fc9", -"9213a8663230599d67b698c815b805c4ed2afded7e32f3298389112496fe22b6", -"9333f7d5e5f20c8a5e190f5c2d47b8c068b57403471614ea6abdb4cad987736e", -"937a72e54843d3420544a0a1e685c105a72b363fbe50bd441b01bfd2a0596c22", -"93b483d7f8e15f43117ed1148c8c07a05c73a7d5a1d7962dae66458dda8052fb", -"93b7a10ad87b0e0fbaf074318f6bf26bf1a0dcbe851b042ec8c310f13bb2bb27", -"94de72f54c1f35683f9a5953ef50d35241bda08eaf4226374dea7861fe394377", -"950bb380139f2970fef060a4b7cb8cab47ac6e894c17db4847214176d71a8309", -"952e76b8bfbf1bd710fcc776958b35458ebbabcb79d7325709f7ad2c4a8c0474", -"9558f303fda9abbc350eb7bb2e21b080881da5e7f45827a9b4277ca1b3d40ac3", -"956c347527141bdb666781019c595e8550f83a8066c1405aa77bbae43ebfa0a9", -"959e3b906efd636d95871bc29f6a24e9ff529a1afdf061910b719c7c3238a63f", -"95bfc307a4cadd5b53237c38ab89732ecba367fc4047f0b28f3142ee7346d893", -"9628ff7531f4b3a6e438aee7f8150cf2df4ca28d0b9021ba701b8d5f2810c0c1", -"974a90358b73e571aeb70bb116e0312c01c2f25e9d4d8ef44ee5aeafc7f8752e", -"97a7b33f98ee755c106c3d74d7bb43d2e0efabd4c8e43e308675649fd160af1d", -"9812897cb1b3a2f9e5456515cfe1da92d65132b09b77a23d32572fee46666e1f", -"981ceabdf18a036f1b31476db256c35e777c530438dac4eb599857e01bad96f3", -"98a5579e0aafb542718d6f10bd045621cbd3e36e86b4c49db0f16358085a6554", -"9953a1bb578cd7cd0080f5ed72baea76501e3e5bf28d01a5f21fc4ea0e59383e", -"998f1941190afcece028c77541b1d1dadf68a9ecaf1b433e8b038356e09d545f", -"99fb87970b354fac1b3faeeadea2d486727b3eaaaed5085f285d6388b74b550b", -"9aaaf49fb60b08bf0b11adb8fba64d46f300a9552e773d09902d65b2346b4d8b", -"9c15b3d83e171d359141375d1c706ee88ca6e649ba5c52e7f5a32a44ce4e7c59", -"9d5731654cf81fbf4de388c078a706891c672a5f42875d416cff6fbc3fb2c0af", -"9db13d4aae4424db994f9e4571d40af396314ac057b4533f7e28f2697f90f60d", -"9e9421d6bb3e5779cc0e2bfeeb7d277026bda087fef458478226865a466e9f5b", -"9ebf590272e80ddac92c929b0c68639863a5c870f02a21ca4e146c5dd825e4b2", -"9f46111ebf5349117903dcc055773dc8d2a3ca746dd1b6f737ce3d18853b00a1", -"a0207f0aab71c10809cc3334e7fad09fb1b184e94e41b71ac2fdd58bc1f33e2f", -"a0ee59d3cdfcf9df11ae2ab11222827ba54d8bdfca4556e2ddef86b6ebf9bc5b", -"a12ee161a767e55b1e7e1a0c341ccacc6807a336b56c752e68c53e476427bef5", -"a1d341eb7ef5ce3ba356ff0894a981fbb54297aca5b6041adc239e9d584d32f2", -"a1e0149a2896d6c374351853f75765f1497d1cfa589f22399d189d30b85873f7", -"a2369b8d3cc1bac5e71845b21d86cf263faa92a76b1578f61d19dc9961cca224", -"a2aa0f2fe085630ec0ef538641d87f54aafe4e15c806af233ddba19a28c82a60", -"a2cd435677b0a4f04805b2bcc74b17f23c339f04a4087425c1ef08fa16638a1e", -"a2f2baa665e9e93af3b0aff4b9e5875cbfe628044d6013445569bef4666f7150", -"a39bc41a07e47ca93e4c7744841af7c978ac3c00c7a8dbfdc4d0a635b29118ce", -"a3b0aacbd06446c64d71572d8913ad8882a6e7949226f71ed331abb4536309cb", -"a3c7f5ff3b31ed2f071f2c76dac1518d59b893a34154a38bdd798ac836646251", -"a40ea43b055a0c07b3e524f3491afb24f408cfd7c2b1ddbb913c6a76574ff0d1", -"a47e62e4910f3842c004d2aaa5d842fe77dcee04c225e7e7e03a4a6a99326efb", -"a4d662d25f921f0cad3c5c8fe6bd07af3493dad59e94db8e1d2b5a6e6a72242d", -"a57cb6d059beb17e15f13e0d318a9d3b06b60ae8651ba389311fbe2ce8327f58", -"a6268893016711ef88f25f4540f3f529cf7a3b56f1c0b862dc569147d54a6885", -"a6a073ab01defd1a1263add0e8e74a726560828a3efc55ae63ebc2b2bfa96cbe", -"a6b136a0d4ddcca72c0c36e2c33463320be85d45bd029345390462079ce8d06a", -"a7d4d13dfc879383b471f4594907fbc37cc7ca2fd84a7cc7c192627fb60d6ce8", -"a8440171ab018bc8cbbe33fbae4bbce86641137ee747c64c48b8df22928f33d3", -"a8c1a26ddb3caeef57146e17cee325f8113aea94e0f71df48a8d84e07f59c9ad", -"a8f40ef2d66582a95bf1ebea2c3ae0f94f4fb7c4dc6a327905df5a50dc1a048a", -"a9c4acc6d55182353d0a5183a646f63d997d747257cd1d84a7cad903f5c0ad24", -"aa11f48e1202e7bd58d0b966f8d9ac210f6c392cb88c3384409eae5bbeec335c", -"aa6503c37b548c8d1c267923e15ad8ae8a9355fafc75ec0df64d0017661b95e4", -"aace9b30c5c3a109a5cd2f3175225d36b1c669326c1b7b45f50565e84bc12a10", -"ab02e6676f649e898716c33a25d4ff41a2e5a1a40f179cc4aacb33577467c1da", -"abb429a5e938e08f1baf136b951072ea1ceed8009e900c74fc68281eb1035df1", -"abe7af27fdf442f39b5bacbbafe746444f915bc0fed63383b56645ca0b09a3fb", -"ac9e883806ff9b8d81b099162488120a5eb1ecd0e67842a621ec80c2559562cd", -"acc3eb4dc225169a38b2adfda95fe88493a8a6c703899a342ba8477e303a82f5", -"acf61bc2bd263e51909eeefb6aab2436d77fcc538bb67f91152886dcf1b30efa", -"ae3e67bf2ffd63bd789cf63b56770c54de1e4dedc067a22b9cc27928b8368eb6", -"aeb2a4c400d2e8632ceca2a14ddc5f88dfc9ce87eff25d72d7ec586c6ab11fd7", -"af3032f6b7c3d57bd743d9fe8475c839fc7d303b9f5e00475dbbffda8f96f2b5", -"b002598b4517ee50d867b9a363267a10c714860e4d1ff91737241af339cc0932", -"b02aa6cbf26e971c7498a779741b3694854195ae5d7efd7e939e937128074690", -"b06ecbc32518a1c58875c0f27b73873f35990f71ea26e391da1451109cceca75", -"b07e9b5685b0df3fc14e4b5aff3e8de3baa6a8e8301a9ee634feb0d2b71eb900", -"b0c6cda501492e5e27959788980d86232bb30cfdc4d66a32cbc737f2fdefc325", -"b1497c0ed6ce5ac86762092d1c297e5c21bc9f7998fdb9f557cc38214902a7eb", -"b17537bd53af132db28aa6cfd90c580d05f855ae703304cf6fdd5f880a982145", -"b19f31eb7afa92cba51aad46d83ac65b9a85863b859527b7903465bd1372dacd", -"b20ef4b33318ec30c99f9e9c04fb4cdeaa2d5d214040332be89cc008432b5cf7", -"b2612b94807d115b5dde40a5e141f3dd2a37668e70a85fe0d7e337a512f53436", -"b2714917337f13edc80694791c23cc2859ed0cb648300506f1916790c5f5c18e", -"b335f8cb071f918e6e69db7ef2a380869ea64ee18e4aea75b61e09d60bdca471", -"b36bc447d30f483cbc401f883793008898f8d21cd0026caa9ef5011ca092e980", -"b387e1b6aebcbe1013dc635b3bb5538b211f05965897ce6a44f4cd452956de54", -"b3885304d53b923dd5dfd8160fb2d96705d346b1fa259527c9a89cd164d69bc5", -"b398d393b60d32498ed4b47e7f5dc9352d6728d3aa2b6f8dddf16efbe9dbf5ca", -"b3bda99fa07b541ef1a6028431a119280204c92aaf748ef44c9eb33494d40e0b", -"b40826ff1790dcb31ca602854d3e74479d033e6ceabbb26f01c18acbf5d14238", -"b422c62611be24b88203d0fedb1d9aaeb048b8310d6dd7418ed44c79a9c9a2d3", -"b4a32f14a5ef694667ecdb96c38ab9b53b05d72c37c8639bad4308ad3c84a485", -"b5075bd276b3c4284ebddb9b35cde17d3392f5bd2cdb3d94c10a39b7daae29a6", -"b5524d0b748f0ce1456e68ee2b2432d3f75941077a491327712a02187eb1ce97", -"b572bd175b76310e97b125bf15347cdc492279005bbec758fd82dd634c47adb3", -"b5b2c6e15e6dbce0d92e6e1c8421f9e2f1b7435201536a1115046f1a5c24ab0d", -"b6139a32ce0ce918faffaccb81eb0af6b7876eb16c45717b2138068d535b94a9", -"b61c5814d897e82027ee54d72916e62cdc9b68e4d8d86e6eca33341a1c07134f", -"b66682319f8ad024cd3b3c95ac8dcd8c867ac42e34183ec62c050bf551f8caf0", -"b688ce77a6a1a05effc123d310d564965380f8e0b2ae83a498f2f1ef925aafea", -"b6c52f67bbe89a9a034def0179791d6aff9302100c6b906c1ca87854c45f7844", -"b769aaae3513a203755fd86a22d08b86f231bd9d6a7ef747c12d080ecf048ec5", -"b76cff990e2d9f9944fd7ffc8a0a14563f9a09b7336e3d6bc5031c4a2e863e0a", -"b7e79a5bbaeca159428eb488309eecf00d31adaa1018302fff2670d8674537c3", -"b85a58e7dbc835b3d730d17ae598cb50c7cc89b63f592d4c006d7f0915e0d4cd", -"b881a44495a3fb0b7b74a1dbeba7a864aaea3be95caaeb9cbbff7e97c95df5a3", -"b8fc45a4def8389cb37729a365e7587691074f379c1c2a27ff3f0c0dd6119461", -"b91b108fd6328800a8c019ae609e4c721f152d3df027b6728ab9ae31126f046d", -"b941eacda36399b92deef7c2e5bf2ecb7a767a10dc75a864c9c790d262884efa", -"b9b775123a94e3d72b2d6e7b03cb630aaeeb557e68502615cb1c4cc2868915c9", -"ba0f2ecfbdc10784a2e7908191a65e5ce71265656c49db0b6688f416783028aa", -"ba2da2413e58d2a4d516c43fb6e4757045f00454e488d64a77dc3de9ab841a13", -"ba3fb6950678dbd56deceb1d223babdc840ab9029e6ae82fbe1b8d8f17661f49", -"bb2e893fc178553b1bc69cb28641d3cb9c8dca4c62f61ca5674899ce7eae434a", -"bdb1eb23f48773ddfc8539f9274e503cf6d254a38eade9f61c3e31e3f29db140", -"be1802fc8945a1a5b8b9ee733950e1363c43b8bf09a221f8d7b0870d5a72e002", -"be3f40bb9d6956124eb960b86b473f97b6d86b40179cc832865e2bafbd2b16d7", -"be8495504f504a16d1f9aacfce97c88188b196fbe0200c9467f21530b715013d", -"bf789f7903f27e97c0d50fd250ad145ef66a4e49bfdf026cb3ae1037937f0b1e", -"bfec5e5f08608628d76b8507d4826838397c3025ac9d83df252e2562611de4f9", -"c04da726f102e44cca0335a4d8545505a858ddb88024ba02c59b65ce9373aff3", -"c0ac1d3c6046d21b0d19f0b938743c6d0611cd198318216ba4127447e8ca4995", -"c0b7b81da30bf715ec43b70fee354b4767ee49fbdc6bb00035f72fb407c53072", -"c0ea3b2416e056d89dc8d4860ad03ee684629e9b951c98b8030b56be0351242a", -"c0f69054fdd368a45dc9df80833ab41b3821ff10261dd3ffc296c5aaa15df7a8", -"c13ef7b0678ed3b4bd541f133d359111a69cd47adb73a3f213eab02171a480a4", -"c144f305a44d0f6149429927ae40992604d5aa6f1cd9a5291b25d2e5caa00e88", -"c2f46d2ab19a37aaa7ff39f9aac18e6d619aff7621b11f2921b44b8b737e2036", -"c40a8cdd03f316cc6360825f4b99c1f744c58f978bcc7c35e12279d3b2062ba2", -"c40d70d00cc546b7fa3b545ca0573899b6e7b3e260354b18276c0f816a1339aa", -"c458699b9b9b5372c3033398343184d3dfeb4c67902a7dfa8e8f399053a7e920", -"c49ec7dd7563e86e51c750ecb1c25e21aaff726724da25d7d1287d7288798e4d", -"c4b83410c0a7a779aa50f80fbe4f85fa9f735b2f41e2d5874f1c994354650e58", -"c538efa7d740b28165ca6d085628dfb6f14cf3b064721be04ba29c3b7be97135", -"c5ef4d5c58fa2fe03a022f20fd910153aaa5e6b22c60eb2f32da274f6007fa56", -"c61d41eaa53d2eb8432616d618835e2a9103fcabed13be28b65d0991b9e91d2f", -"c6a4029e37012eb65a4834094caeb6cd8059e1769234094a512751fe61ff5bd2", -"c6d6f3b087fc21c04443c2718de3a849740d34da6226276fcac7e79e117616c1", -"c6e9fbda0064ffa2d91cea1c0d414b61b79646f9706724959feb6d9b6885db55", -"c79444b73c2be39d31938dc476dd4ead5474600dc50330d2e00cf1aef0f44d47", -"c8956ee9b4b2f22bffb2e1313b978851a2ba40dfdb7d6a900bee53c0063c8093", -"c89d71a6f659a0690de6c7ae81a2fb7d25aef0b10864e7a1c0401cd79172da23", -"c8f06411299302030dc94df149e12f8db5267efa86572067dca592ef0ae72f97", -"c90e1f2f62b4df1ee054e6fa468d904f144f15d923f1a167ac591bf5215376e3", -"ca1effebd6ceec9fd29af3da34c7b519020fa366f5bb209506659d201649b526", -"ca2a75809bbcc7b11b3106d767c0b5c4e61fad3033abfd80b8c7024f1b50760c", -"cb169159b89b98dcbb1c0be75539be137ca39ed69d7f0c0df03f5b27e062da20", -"cb95970cbb382ab5af8b45c194ee0a386e313566cac0d32ac76d11b2fc5a67e6", -"cbffa46d6d8eff49613e586ed9cefd6d50dca7c632e489fe9a5db5e19c6f8646", -"cd17e0713701812f08ac92c3b43f6cb3ebdb81f07ba1524e81ea668f255f84f5", -"ce2153385a3abd393e48ef7aa9a487ea097af13b6b6f778c198b723bc0f51fae", -"cf17e3c4c9f4985f29abf2bf19222d0531ddafa0ebe18b74d05a89d9028d2eaf", -"cfa473fec3999e51337fed325c6da1df7f027a9f97b1de049fd2c196e97ae755", -"d0f17caa9b401929f9333073e5ab4421e9cd0e39b5946d3894a533fe66256eed", -"d10fcb346876bd804d44f22a3cfd5ebc7ded0ba4f5f0c3d0d50de92c1429b516", -"d1a357079b7ff24706600fdd45eb12f1d6704bcd4281f09de4a9ab41adf3d419", -"d2c006d6ca537749c985e04b830ce17d47fd42e7a2ab1a6123c03d3db2838ea0", -"d2ff7fdbc1cab96ae8a4ade63179a29faef0825ab296615fc353704263281146", -"d332bc97043c94f72604b31cf4509f306cb2f3a6822c68db7985d63e9ae1587a", -"d367a15459b7c4280a9392beb3b3670ebcee1a296e5ef1e14d4acb83af98fff9", -"d37c4b5f657fe98c2e40d22d049163a379ba3697fb6c1e9b27fcd8c2944a5d22", -"d413fe20342df3c8621929fabe4f50fe885b13e719dcb1f12eba54b56fd6ecfb", -"d438996281c527e53bd2ff73faa25462f0191a2bc9fdf3c0e7d4bb5a458da050", -"d4f530b0ad515731c628ecf70aa12919b861de2244607811f9e74902e5891b4b", -"d52cfa54686e2fae7db1c391b754f52e12c2e7eff3b90cf9aed210e188d98a86", -"d5405d840b7d738be0e401d42b9c2d6c6e5a36a7a05357178036c0a3500d7c47", -"d6138be7bd511588f2f3cdeaf37c1fb26f1aa051930170834c37adc8c72204fe", -"d614cecff549b32d73f5fa72139c575f852629cb7c53835d6da831736145b170", -"d6d52bb3c8965a95b8520b5921dfac30a312d8db4c35e7deac679de073549970", -"d6dadd094d818d1aa19078dc418f3c11ac97b36bd857f761d39ffca55c59a98e", -"d73d18497cc888713655ee65b3525a502b9758196b0d6d3f81509f1c78f3d347", -"d78c0329d621cb71cc6a30d3e56bc98a4205c1741fcacc450c264acce9fefee9", -"d8f865a34c5d20d0441ca56df7744bc2f3f86412787f4210c5fe24f36cb22a97", -"d9441a7825585aa7ce4a1db1f52eb0b2e8e06afc53d76c07c5d34c19ff7b3eef", -"d9970b9029331ec5b646bb2c00ab8bf025218b14df46742f1db7184d9a7f639c", -"d9c6622cb19d520d7ed30682c65b82afe1afb02af408159255aa9b28aa003f2b", -"da702f1071941958f446f887e753298b9d5600631abfaa2914ef789fdc0c6b6d", -"da90c6cbf2137cdeb222504b5aab13f5745764e56a2a9dca5bf6335d0ba27653", -"dab55adca8d2e7979a221886216a060389a8c28eb17a4699e924f5175c5a2e8c", -"dafed9f1c7c62859810717b8c39e45c6d38d06f82d526e56eb5686fb461a7420", -"dbe966f0bf291d2f852ebce75b722282f2e78b45be2d4a7ef2475626cd1d5e29", -"dbf0ac3bf82be34f2ab3261eff345c8160f5ba263e5df596f4165ff3f05a476b", -"dbf4defb38fb50f3673df83415855b125656f98ce0b4cbd9ff945eab84544617", -"dc549ae979919af281eaa66e0b77589a54c868640665ca1a7f8e7d6d8b2777d6", -"dc5ae132157f07517d9bbf436f85c7d06d383e67974a0e931126ea6ae6b0604b", -"ddf8fe65ff1bb1471dcf50d9c9582f4d2e45172ac403f795f94c93603d076dfc", -"de17982b2b795a5a76635598fef165bad28b6eddefa953c5eab7c2c58099fb79", -"dec3fd28760fb4371259c11bd6a3c8da74209ed879fcfea85f3c5bbfd90db0c0", -"deeb6289c34666860513c7f766cfd16160e4aee511f7fcfbee44c2e56478517b", -"df3f12c0246146c55fc971b81b34d033ec768f1d1e7e82750cdf8bba7431f246", -"df9059d9f7f24219805a11ea50b73433d8a6ce7cf4af31fdc2d9ad7ab10db80f", -"e09e77de453a208767d35955ef273072743c332d4eadc7a444a31d95d334af29", -"e0cb2e68da5c2a22d1f611050460d27bdf120fcfcfe0b6d0a501ee7d5a599782", -"e0fd6a49a89689b5b7a9ffbb0c024e2e549e827caed6df896b1743cba7931bd1", -"e1571f2032c913fe8d466a05f81141c8a78e63b2c4955f607ff84bb8f3276c8a", -"e1d0595e5c8f2aa948daac642306023db0c34233fb4693f59d87f91f83765ed8", -"e32b41c1f421339238bd8518972651f4b860eaf9d613bde723041ab877c4fbf2", -"e33321c913f6af442dedc4c8f6ccb44286bb4fc1bf3a9ed3e36c3c6fc71ea5bb", -"e342c5f98ddc557fb938d46ac99579e5c927ed2929db4ecc0b7aecf9cc8f1931", -"e350819b29bdbb2a4218fe99b12bcac5ae9afe2504c3586e21627eb497621e1d", -"e37c436163b831cac6284eb3e6cb9848946e756bb639705279f72dc84e4c4ff3", -"e39edf70ec8f6d81b566a6907df63bf8b7ca85e453d71273d3a34f75e717f9e3", -"e3d61a55c9dfc335ca2be1ecc0bac39ffcf17c621b5805505128bcfb44407623", -"e3fb67b8d5d0844433f2306901a26196669e047160cc96d4641e87fadea0c408", -"e47dc8fbf8286a8d1b85769c05d3434950ff59e12c05cf5fe89144513fec2e98", -"e48ed7b1f444c282edce46e373d2c670415d18d798cee113739b094863c9dc56", -"e4c91ad52da7d4b7f582e8772aadbd311499f70098af0ab194fa6053829e7f03", -"e57dbec2d57d240f74308e8b5f59a6301efd74ae081a6a8eaefd0d3bd29a4190", -"e5bf7ba6b543615982d70d8fc785c9c1639220c1de284ab1de43a34f1de3fdc2", -"e5c80a3cf4e56ce81db7c5fa776925d26e8d5a841416d7ed919cfb24c407b158", -"e60d8df101933e7802a456df4eeb45b98f8d7db511dd9975cf6704bb2f30339c", -"e64ba5e7b479b3ad54b7b9333e5053cdd43930ec5adcc8ea37dfc4f42612dc72", -"e6955a326839da77307fe806b976975c9e2c4c93d237f9667abe9b69ce74cabe", -"e695bc41eb0ffa1c2dadb390e5294522bb5d0e42802fb7b47957b0f5aa9789f2", -"e6a132751e0e55e969a5ceab62483fcb85d06380a37e912e4c5953a476ddec40", -"e6abf1b643cb676f6bb587cab4dc04e607c3fb0d4c142758b90946fff137ee7a", -"e6bcbc7f692c3f9c847af822735461d83007f9a09956dc2f71b2af98e1d1d380", -"e7088919ca800eb174780758a6e656712af61bd7b9e4aa39fc276d9696dfcf85", -"e7137a074ac85291ac4ec39bb06103521225fa05d3c2269b4967afd6b81680c1", -"e7f192929ead8728d678cdf4692106b3427812b23a54e0fa8159d4942f08516f", -"e81b838b47cccd2a490884e7d79d2cc2a10cb6be50b90f11dad729a408af107c", -"e8f1bb9ae9d51ee27c1a437ec70e07d1e5d4b85e1b0702f0f1defc2a03b9a815", -"e908a268e0134959707d8d15582b97f9faa86f80c3448972db640665c5f58831", -"e98e80fa5977235c6895b411b6518ab1d084d09a097efa97a72c070bc242d85d", -"e9a9f260d31636451f7c188851bb54c9e7208b413b927a623296791e13dcf8ee", -"ea2882ddc2f7824741671b9b8e703f0011a64137652cdacca940ffe538585755", -"eb16d0c1567c34dc4217e2916bec37c934bf9c7c71f1768960990a563fc273e1", -"eb5a783ff603ae878af092e4b66e0bc22ee10f2768daafa5e4c0a1cfaa0eb547", -"eb96d352e986039713b0ab96c050193006909b1fc96ed10d0828ec84124df621", -"ebb656612ebd8f15df6682db342d57f02409ec9395edc5a39fe01f3c330552e6", -"ec692aab6dff6825a05baf7c90737dc2b66d1b1ce13c25d61258f3702fcc9403", -"ed77a1a1da00c4dfe100a8c656ff1d8336d5982b11a8a0c8236ebc0272fa631d", -"edcff1388f91fd80a2b62ec31cb0492960ffb62206dd9e01d8690dd24ca7cf7b", -"ee194bbbdf6b9ae5405d1e2565427a50c93b2fe7b9ac5c525fa6f2cde67c7e51", -"ee1e01ec7a5e9893fcf8470aea62c03943c795d3b271a7d1f764a255648b4b7b", -"ee717e1a1ce1162ca1bf48a49874ac3a07825c0a131a928d5f647fa80ea19177", -"eea120c9a37cae64352df0b7a0223d8ed1a96561c8c1c6558e5dfe1242969c3b", -"eeb8a491df8df8d1369af7f56e84301abd465a30284b4c9d9b3742faaa21f2a7", -"eed3f14a42ad30a4895ac71dfb66f4152bfea5f6eee2a50097aa28320400915b", -"eef086f911b7483149da38088c50a19208409156074d37f787b86d3f2e1b00da", -"eefd25a321c6c29532a0971af7e28c949c0afdbc2bd005311db2bcb8b7c2c7bf", -"ef1affc22033b8c329eea7de39ae99a4c206ce7cb13a0ae18287d027daf8443d", -"efa335271b61f6468fdeba3e8f84a9909b5d10eed195172368045e05548979c0", -"efd61d23aaa7ccc95a8413373e2c93465a77dd934207fd093e876f9cbbf6527b", -"f02249c03a3d62665d33c54336f647b643fd4e86869dcfd957ea498b5a3bab1f", -"f10193b34fa878cb3828e7df1d56537afc19d20f7c3749b8ef2a3da02335da0d", -"f15cd0e24e9956c7e19f4327b1e162aa41c83442df251382adb7b14403bdbac6", -"f17b282c97be3d57cc033235cb2c36ed993b3a2983561c8790478a146a8d79d2", -"f1cba5c8848c8024f4bcb86f09a6d698349a01a9eb7ccc146ee395e3c34140dd", -"f20d92b13b27733d411c2676486ef7281a72da05f8b29c8c06ff2877eb14c35e", -"f2bd54d61f8b12a7f2305f0df2125c659c71f6b0eecb65911db05a6f4f91a50f", -"f2ca83b3ca7f974607cb5285699b42002bbda937a03bc7d8b76227f5fc0dfb75", -"f2d3624cc81db6dd6a6529f801739066e24ba332e3a85d5af0769f3598338eb6", -"f2f519e3c719d330ddbfba05fdc5368cda0ac65536fc34555cd9615e0c4624db", -"f31fddc0966b0561b2aa5d6436732457252eff51dda4022d83713c4a597b67a6", -"f35daff8db8ff3e286a5b4704c7c24c954dc11ec177aec756c613c3461f51ac2", -"f39428f690c49d3c4dd5f428f710b3608d7e75be2ab316c091c80ed31aad2a5a", -"f3d84962adcbd6a03ac169a004a0aee70e6e49d967ce10c934c693c1babd5e37", -"f45b82f84bb1b477447cc9128cc7a51797f28441a89dfdcbb17df6dd6a0da492", -"f45ef3feae28bb0ff5600cca630ab000acad15e8296d97e4cbfd425b4bce1387", -"f503813b48f51d5712d6bb64828a3e14877b9d5f40b7722df4c2ceac1ef5f9c5", -"f632dd3b7a4e36306ae504ab2dbf2dc4f171c0ec33fa702cc30642ddf6aab789", -"f79256f68b2011712d0d074248876caccf98614ec1d15d84ecd4a5a7f02576ae", -"f79373cbf026b4737dc9a5e69bb71112a91d2fcd602b93b7b9cae3baee4c5804", -"f7e3344ae2b06b8bd3596b54930b6af055c515b4f9dbb839bc63b1bb13e5efd8", -"f8450815db8e2e8695db5da777fa053a80ff80f747175c6653df031211bc6607", -"f87c92566596fff9083a67d6a810575da0b01a63a8c6c6026b1d120f548d6951", -"f890ed99e394d322712db3d9aaeef36710167dbf6c71a31a13e1158efaeee755", -"f8d924bb9bf40a313c2c160894aaa3b75294be55c349198ec380706cd6997b90", -"f91848f4e7fce0036d2bf207f9488ada0c3d0d8a24ff22bbea60e9d331e25f41", -"f987b68f789b31eb610e2552bbfeb1cf6c7b0c962109b3be28bb079c26eea9a9", -"f9adc5269a86f7f5df0bdcd20267bd2723c6f66bf0704879019af4cc85db193f", -"f9b21276c4fc9b6f3de673b2b2c3176dfe0594155f4046e28bbcd83236fce667", -"f9b3968c19691e8db10bde4bd06fde35f0d081e7ee9e10cb0cd320bc20558f96", -"f9dd719319a9bc015d0a80a32589873603808a386df04e5a5372cdb9894704b1", -"fa278d51fc6711b3d14ec7d230cc7f1b2fb57a894d5b1284d1f73b9643b31f87", -"fb284598c4c59f69abd77e0222a5d723e44bb217f34176151b377df92c0bcfe3", -"fb6bbad84d35d47a49eaf3fba088228d299f9efc792e96c8ff6910d4a8fbbb6c", -"fb86bd928ef2b1ecd9918cf427a3a91e24074723922bf8e53e616f698c70c38f", -"fc1d38a9ce46a7fb8b0b0f0cc0ac3a5f802d663ab5f4c2aa5a76ba885f78f6a3", -"fc68a61bbca0c9625be8a30080353cd71fbf6f53b5e02852063c2ecd7c591cd5", -"fcca7e635caa12b17c68ad65b9c666b339232c0a963eb882d4a17ce7a6b7c509", -"fd00432172be122a36bd1fd86a1a200b14cadba9b3f38ffa361b933df9293c10", -"fdad07cf96335392e4b7c8004cfc1f34835c59b66827bb05a735f676218c72b3", -"fdd5a1169ab44e2cfce3a8609d63e88386481f149a198f9e956276e7598ba3f7", -"fe0e2a3d68aec63785b880811ccf19a70893cf9901e48036dcb300f49b2fdb7b", -"fe26826593168d80cc58e86dd22161129f538d65751fbbff81adba425fba4e05", -"feb3400d62fe9a150da8ca8c930f3eae195b3a8f99467d88c5b5a9bf525e8d3a", -"ff82ff74e5cbd2b06468133d88bcc5d260cc7a3de73fb35192dcc87e78cee9f2", -"ff90e9d931b23add9b25c3c4e5b1f4d4623622aca888ccf18e59c96fd43b5a28", -"ffdeaffdaa820f860be1e0d7722c12b8213f93f7b0292f15e835408ac4e45d45", -"fff0ad60c32b99edadf3d327393fec3e5ef0ed476666528d8b86ce587fab4f23", -"fff6592657f29d91d6f9e3fe76de2eec9d461173cd7248a9ff2f065393234bb5", -}; diff --git a/src/veruslaunch.h b/src/veruslaunch.h deleted file mode 100644 index de081e11d..000000000 --- a/src/veruslaunch.h +++ /dev/null @@ -1,17 +0,0 @@ - -// Copyright (c) 2018 The Verus developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef VERUS_LAUNCH_H -#define VERUS_LAUNCH_H - -#include - -#define WHITELIST_COUNT 704 - -extern const char *whitelist_ids[WHITELIST_COUNT]; -extern const char *whitelist_address; -extern uint64_t whitelist_masks[WHITELIST_COUNT]; - -#endif diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index 04af76211..c77489381 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -34,13 +34,15 @@ using namespace libzcash; +extern UniValue sendrawtransaction(const UniValue& params, bool fHelp); + int mta_find_output(UniValue obj, int n) { UniValue outputMapValue = find_value(obj, "outputmap"); if (!outputMapValue.isArray()) { throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation"); } - + UniValue outputMap = outputMapValue.get_array(); assert(outputMap.size() == ZC_NUM_JS_OUTPUTS); for (size_t i = 0; i < outputMap.size(); i++) { @@ -48,63 +50,75 @@ int mta_find_output(UniValue obj, int n) return i; } } - + throw std::logic_error("n is not present in outputmap"); } AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress( - CMutableTransaction contextualTx, - std::vector utxoInputs, - std::vector noteInputs, - MergeToAddressRecipient recipient, - CAmount fee, - UniValue contextInfo) : - tx_(contextualTx), utxoInputs_(utxoInputs), noteInputs_(noteInputs), - recipient_(recipient), fee_(fee), contextinfo_(contextInfo) + boost::optional builder, + CMutableTransaction contextualTx, + std::vector utxoInputs, + std::vector sproutNoteInputs, + std::vector saplingNoteInputs, + MergeToAddressRecipient recipient, + CAmount fee, + UniValue contextInfo) : +tx_(contextualTx), utxoInputs_(utxoInputs), sproutNoteInputs_(sproutNoteInputs), +saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), contextinfo_(contextInfo) { if (fee < 0 || fee > MAX_MONEY) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range"); } - - if (utxoInputs.empty() && noteInputs.empty()) { + + if (utxoInputs.empty() && sproutNoteInputs.empty() && saplingNoteInputs.empty()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "No inputs"); } - + if (std::get<0>(recipient).size() == 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Recipient parameter missing"); } - + + if (sproutNoteInputs.size() > 0 && saplingNoteInputs.size() > 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress"); + } + + if (sproutNoteInputs.size() > 0 && builder) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Sprout notes are not supported by the TransactionBuilder"); + } + + isUsingBuilder_ = false; + if (builder) { + isUsingBuilder_ = true; + builder_ = builder.get(); + } + toTaddr_ = DecodeDestination(std::get<0>(recipient)); isToTaddr_ = IsValidDestination(toTaddr_); isToZaddr_ = false; - + if (!isToTaddr_) { auto address = DecodePaymentAddress(std::get<0>(recipient)); if (IsValidPaymentAddress(address)) { isToZaddr_ = true; - // TODO: Add Sapling support. For now, return an error to the user. - if (boost::get(&address) == nullptr) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Currently, only Sprout zaddrs are supported"); - } toPaymentAddress_ = address; } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address"); } } - + // Log the context info i.e. the call parameters to z_mergetoaddress if (LogAcceptCategory("zrpcunsafe")) { LogPrint("zrpcunsafe", "%s: z_mergetoaddress initialized (params=%s)\n", getId(), contextInfo.write()); } else { LogPrint("zrpc", "%s: z_mergetoaddress initialized\n", getId()); } - + // Lock UTXOs lock_utxos(); lock_notes(); - + // Enable payment disclosure if requested - paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", false); + paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true); } AsyncRPCOperation_mergetoaddress::~AsyncRPCOperation_mergetoaddress() @@ -118,12 +132,12 @@ void AsyncRPCOperation_mergetoaddress::main() unlock_notes(); return; } - + set_state(OperationStatus::EXECUTING); start_execution_clock(); - + bool success = false; - + #ifdef ENABLE_MINING #ifdef ENABLE_WALLET GenerateBitcoins(false, NULL, 0); @@ -131,7 +145,7 @@ void AsyncRPCOperation_mergetoaddress::main() GenerateBitcoins(false, 0); #endif #endif - + try { success = main_impl(); } catch (const UniValue& objError) { @@ -152,23 +166,23 @@ void AsyncRPCOperation_mergetoaddress::main() set_error_code(-2); set_error_message("unknown error"); } - + #ifdef ENABLE_MINING #ifdef ENABLE_WALLET - GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 0)); + GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1)); #else - GenerateBitcoins(GetBoolArg("-gen", false), GetArg("-genproclimit", 0)); + GenerateBitcoins(GetBoolArg("-gen", false), GetArg("-genproclimit", 1)); #endif #endif - + stop_execution_clock(); - + if (success) { set_state(OperationStatus::SUCCESS); } else { set_state(OperationStatus::FAILED); } - + std::string s = strprintf("%s: z_mergetoaddress finished (status=%s", getId(), getStateAsString()); if (success) { s += strprintf(", txid=%s)\n", tx_.GetHash().ToString()); @@ -176,10 +190,10 @@ void AsyncRPCOperation_mergetoaddress::main() s += strprintf(", error=%s)\n", getErrorMessage()); } LogPrintf("%s", s); - + unlock_utxos(); // clean up unlock_notes(); // clean up - + // !!! Payment disclosure START if (success && paymentDisclosureMode && paymentDisclosureData_.size() > 0) { uint256 txidhash = tx_.GetHash(); @@ -202,12 +216,12 @@ void AsyncRPCOperation_mergetoaddress::main() bool AsyncRPCOperation_mergetoaddress::main_impl() { assert(isToTaddr_ != isToZaddr_); - - bool isPureTaddrOnlyTx = (noteInputs_.empty() && isToTaddr_); + + bool isPureTaddrOnlyTx = (sproutNoteInputs_.empty() && saplingNoteInputs_.empty() && isToTaddr_); CAmount minersFee = fee_; - + size_t numInputs = utxoInputs_.size(); - + // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0); { @@ -221,40 +235,46 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() strprintf("Number of transparent inputs %d is greater than mempooltxinputlimit of %d", numInputs, limit)); } - + CAmount t_inputs_total = 0; for (MergeToAddressInputUTXO& t : utxoInputs_) { t_inputs_total += std::get<1>(t); } - + CAmount z_inputs_total = 0; - for (MergeToAddressInputNote& t : noteInputs_) { + for (const MergeToAddressInputSproutNote& t : sproutNoteInputs_) { z_inputs_total += std::get<2>(t); } - + + for (const MergeToAddressInputSaplingNote& t : saplingNoteInputs_) { + z_inputs_total += std::get<2>(t); + } + CAmount targetAmount = z_inputs_total + t_inputs_total; - + if (targetAmount <= minersFee) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strprintf("Insufficient funds, have %s and miners fee is %s", FormatMoney(targetAmount), FormatMoney(minersFee))); } - + CAmount sendAmount = targetAmount - minersFee; - + // update the transaction with the UTXO inputs and output (if any) - CMutableTransaction rawTx(tx_); - for (MergeToAddressInputUTXO& t : utxoInputs_) { - CTxIn in(std::get<0>(t)); - rawTx.vin.push_back(in); + if (!isUsingBuilder_) { + CMutableTransaction rawTx(tx_); + for (const MergeToAddressInputUTXO& t : utxoInputs_) { + CTxIn in(std::get<0>(t)); + rawTx.vin.push_back(in); + } + if (isToTaddr_) { + CScript scriptPubKey = GetScriptForDestination(toTaddr_); + CTxOut out(sendAmount, scriptPubKey); + rawTx.vout.push_back(out); + } + tx_ = CTransaction(rawTx); } - if (isToTaddr_) { - CScript scriptPubKey = GetScriptForDestination(toTaddr_); - CTxOut out(sendAmount, scriptPubKey); - rawTx.vout.push_back(out); - } - tx_ = CTransaction(rawTx); - + LogPrint(isPureTaddrOnlyTx ? "zrpc" : "zrpcunsafe", "%s: spending %s to send %s with fee %s\n", getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee)); LogPrint("zrpc", "%s: transparent input: %s\n", getId(), FormatMoney(t_inputs_total)); @@ -265,13 +285,134 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(sendAmount)); } LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee)); - + // Grab the current consensus branch ID { LOCK(cs_main); consensusBranchId_ = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); } - + + /** + * SCENARIO #0 + * + * Sprout not involved, so we just use the TransactionBuilder and we're done. + * + * This is based on code from AsyncRPCOperation_sendmany::main_impl() and should be refactored. + */ + if (isUsingBuilder_) { + builder_.SetFee(minersFee); + + + for (const MergeToAddressInputUTXO& t : utxoInputs_) { + COutPoint outPoint = std::get<0>(t); + CAmount amount = std::get<1>(t); + CScript scriptPubKey = std::get<2>(t); + builder_.AddTransparentInput(outPoint, scriptPubKey, amount); + } + + boost::optional ovk; + // Select Sapling notes + std::vector saplingOPs; + std::vector saplingNotes; + std::vector expsks; + for (const MergeToAddressInputSaplingNote& saplingNoteInput: saplingNoteInputs_) { + saplingOPs.push_back(std::get<0>(saplingNoteInput)); + saplingNotes.push_back(std::get<1>(saplingNoteInput)); + auto expsk = std::get<3>(saplingNoteInput); + expsks.push_back(expsk); + if (!ovk) { + ovk = expsk.full_viewing_key().ovk; + } + } + + // Fetch Sapling anchor and witnesses + uint256 anchor; + std::vector> witnesses; + { + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->GetSaplingNoteWitnesses(saplingOPs, witnesses, anchor); + } + + // Add Sapling spends + for (size_t i = 0; i < saplingNotes.size(); i++) { + if (!witnesses[i]) { + throw JSONRPCError(RPC_WALLET_ERROR, "Missing witness for Sapling note"); + } + assert(builder_.AddSaplingSpend(expsks[i], saplingNotes[i], anchor, witnesses[i].get())); + } + + if (isToTaddr_) { + if (!builder_.AddTransparentOutput(toTaddr_, sendAmount)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid output address, not a valid taddr."); + } + } else { + std::string zaddr = std::get<0>(recipient_); + std::string memo = std::get<1>(recipient_); + std::array hexMemo = get_memo_from_hex_string(memo); + auto saplingPaymentAddress = boost::get(&toPaymentAddress_); + if (saplingPaymentAddress == nullptr) { + // This should never happen as we have already determined that the payment is to sapling + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Could not get Sapling payment address."); + } + if (saplingNoteInputs_.size() == 0 && utxoInputs_.size() > 0) { + // Sending from t-addresses, which we don't have ovks for. Instead, + // generate a common one from the HD seed. This ensures the data is + // recoverable, while keeping it logically separate from the ZIP 32 + // Sapling key hierarchy, which the user might not be using. + HDSeed seed; + if (!pwalletMain->GetHDSeed(seed)) { + throw JSONRPCError( + RPC_WALLET_ERROR, + "AsyncRPCOperation_sendmany: HD seed not found"); + } + ovk = ovkForShieldingFromTaddr(seed); + } + if (!ovk) { + throw JSONRPCError(RPC_WALLET_ERROR, "Sending to a Sapling address requires an ovk."); + } + builder_.AddSaplingOutput(ovk.get(), *saplingPaymentAddress, sendAmount, hexMemo); + } + + + // Build the transaction + auto maybe_tx = builder_.Build(); + if (!maybe_tx) { + throw JSONRPCError(RPC_WALLET_ERROR, "Failed to build transaction."); + } + tx_ = maybe_tx.get(); + + // Send the transaction + // TODO: Use CWallet::CommitTransaction instead of sendrawtransaction + auto signedtxn = EncodeHexTx(tx_); + if (!testmode) { + UniValue params = UniValue(UniValue::VARR); + params.push_back(signedtxn); + UniValue sendResultValue = sendrawtransaction(params, false); + if (sendResultValue.isNull()) { + throw JSONRPCError(RPC_WALLET_ERROR, "sendrawtransaction did not return an error or a txid."); + } + + auto txid = sendResultValue.get_str(); + + UniValue o(UniValue::VOBJ); + o.push_back(Pair("txid", txid)); + set_result(o); + } else { + // Test mode does not send the transaction to the network. + UniValue o(UniValue::VOBJ); + o.push_back(Pair("test", 1)); + o.push_back(Pair("txid", tx_.GetHash().ToString())); + o.push_back(Pair("hex", signedtxn)); + set_result(o); + } + + return true; + } + /** + * END SCENARIO #0 + */ + + /** * SCENARIO #1 * @@ -288,16 +429,16 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() /** * END SCENARIO #1 */ - - + + // Prepare raw transaction to handle JoinSplits CMutableTransaction mtx(tx_); crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_); mtx.joinSplitPubKey = joinSplitPubKey_; tx_ = CTransaction(mtx); std::string hexMemo = std::get<1>(recipient_); - - + + /** * SCENARIO #2 * @@ -305,18 +446,18 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() * * We only need a single JoinSplit. */ - if (noteInputs_.empty() && isToZaddr_) { + if (sproutNoteInputs_.empty() && isToZaddr_) { // Create JoinSplit to target z-addr. MergeToAddressJSInfo info; info.vpub_old = sendAmount; info.vpub_new = 0; - + JSOutput jso = JSOutput(boost::get(toPaymentAddress_), sendAmount); if (hexMemo.size() > 0) { jso.memo = get_memo_from_hex_string(hexMemo); } info.vjsout.push_back(jso); - + UniValue obj(UniValue::VOBJ); obj = perform_joinsplit(info); sign_send_raw_transaction(obj); @@ -325,24 +466,20 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() /** * END SCENARIO #2 */ - - + + // Copy zinputs to more flexible containers - std::deque zInputsDeque; - for (auto o : noteInputs_) { - // TODO: Add Sapling support. For now, return an error to the user. - if (boost::get(&std::get<3>(o)) == nullptr) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Currently, only Sprout zaddrs are supported"); - } + std::deque zInputsDeque; + for (const auto& o : sproutNoteInputs_) { zInputsDeque.push_back(o); } - + // When spending notes, take a snapshot of note witnesses and anchors as the treestate will // change upon arrival of new blocks which contain joinsplit transactions. This is likely // to happen as creating a chained joinsplit transaction can take longer than the block interval. { LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto t : noteInputs_) { + for (auto t : sproutNoteInputs_) { JSOutPoint jso = std::get<0>(t); std::vector vOutPoints = {jso}; uint256 inputAnchor; @@ -351,7 +488,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() jsopWitnessAnchorMap[jso.ToString()] = MergeToAddressWitnessAnchorData{vInputWitnesses[0], inputAnchor}; } } - + /** * SCENARIO #3 * @@ -370,12 +507,12 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0 bool vpubOldProcessed = false; // updated when vpub_old for taddr inputs is set in first joinsplit bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit - + // At this point, we are guaranteed to have at least one input note. // Use address of first input note as the temporary change address. - SproutSpendingKey changeKey = boost::get(std::get<3>(zInputsDeque.front())); + SproutSpendingKey changeKey = std::get<3>(zInputsDeque.front()); SproutPaymentAddress changeAddress = changeKey.address(); - + CAmount vpubOldTarget = 0; CAmount vpubNewTarget = 0; if (isToTaddr_) { @@ -387,16 +524,16 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() vpubOldTarget = t_inputs_total - minersFee; } } - + // Keep track of treestate within this transaction boost::unordered_map intermediates; std::vector previousCommitments; - + while (!vpubNewProcessed) { MergeToAddressJSInfo info; info.vpub_old = 0; info.vpub_new = 0; - + // Set vpub_old in the first joinsplit if (!vpubOldProcessed) { if (t_inputs_total < vpubOldTarget) { @@ -407,30 +544,30 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() info.vpub_old += vpubOldTarget; // funds flowing from public pool vpubOldProcessed = true; } - + CAmount jsInputValue = 0; uint256 jsAnchor; std::vector> witnesses; - + JSDescription prevJoinSplit; - + // Keep track of previous JoinSplit and its commitments if (tx_.vjoinsplit.size() > 0) { prevJoinSplit = tx_.vjoinsplit.back(); } - + // If there is no change, the chain has terminated so we can reset the tracked treestate. if (jsChange == 0 && tx_.vjoinsplit.size() > 0) { intermediates.clear(); previousCommitments.clear(); } - + // // Consume change as the first input of the JoinSplit. // if (jsChange > 0) { LOCK2(cs_main, pwalletMain->cs_wallet); - + // Update tree state with previous joinsplit SproutMerkleTree tree; auto it = intermediates.find(prevJoinSplit.anchor); @@ -439,7 +576,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() } else if (!pcoinsTip->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) { throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); } - + assert(changeOutputIndex != -1); boost::optional changeWitness; int n = 0; @@ -457,34 +594,34 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() } jsAnchor = tree.root(); intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries) - + // Decrypt the change note's ciphertext to retrieve some data we need ZCNoteDecryption decryptor(changeKey.receiving_key()); auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey); try { SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( - decryptor, - prevJoinSplit.ciphertexts[changeOutputIndex], - prevJoinSplit.ephemeralKey, - hSig, - (unsigned char)changeOutputIndex); - + decryptor, + prevJoinSplit.ciphertexts[changeOutputIndex], + prevJoinSplit.ephemeralKey, + hSig, + (unsigned char)changeOutputIndex); + SproutNote note = plaintext.note(changeAddress); info.notes.push_back(note); info.zkeys.push_back(changeKey); - + jsInputValue += plaintext.value(); - + LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", getId(), FormatMoney(plaintext.value())); - + } catch (const std::exception& e) { throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what())); } } - - + + // // Consume spendable non-change notes // @@ -495,13 +632,13 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() uint256 inputAnchor; int numInputsNeeded = (jsChange > 0) ? 1 : 0; while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) { - MergeToAddressInputNote t = zInputsDeque.front(); + MergeToAddressInputSproutNote t = zInputsDeque.front(); JSOutPoint jso = std::get<0>(t); SproutNote note = std::get<1>(t); CAmount noteFunds = std::get<2>(t); - SproutSpendingKey zkey = boost::get(std::get<3>(t)); + SproutSpendingKey zkey = std::get<3>(t); zInputsDeque.pop_front(); - + MergeToAddressWitnessAnchorData wad = jsopWitnessAnchorMap[jso.ToString()]; vInputWitnesses.push_back(wad.witness); if (inputAnchor.IsNull()) { @@ -509,13 +646,13 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() } else if (inputAnchor != wad.anchor) { throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor"); } - + vOutPoints.push_back(jso); vInputNotes.push_back(note); vInputZKeys.push_back(zkey); - + jsInputValue += noteFunds; - + int wtxHeight = -1; int wtxDepth = -1; { @@ -537,13 +674,13 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() wtxHeight, wtxDepth); } - + // Add history of previous commitments to witness if (vInputNotes.size() > 0) { if (vInputWitnesses.size() == 0) { throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); } - + for (auto& optionalWitness : vInputWitnesses) { if (!optionalWitness) { throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null"); @@ -559,20 +696,20 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() } witnesses.push_back(w); } - + // The jsAnchor is null if this JoinSplit is at the start of a new chain if (jsAnchor.IsNull()) { jsAnchor = inputAnchor; } - + // Add spendable notes as inputs std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes)); std::copy(vInputZKeys.begin(), vInputZKeys.end(), std::back_inserter(info.zkeys)); } - + // Accumulate change jsChange = jsInputValue + info.vpub_old; - + // Set vpub_new in the last joinsplit (when there are no more notes to spend) if (zInputsDeque.empty()) { assert(!vpubNewProcessed); @@ -587,10 +724,10 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() // If we are merging to a t-addr, there should be no change if (isToTaddr_) assert(jsChange == 0); } - + // create dummy output info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new - + // create output for any change if (jsChange > 0) { std::string outputType = "change"; @@ -604,31 +741,30 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() } } info.vjsout.push_back(jso); - + LogPrint("zrpcunsafe", "%s: generating note for %s (amount=%s)\n", getId(), outputType, FormatMoney(jsChange)); } - + obj = perform_joinsplit(info, witnesses, jsAnchor); - + if (jsChange > 0) { changeOutputIndex = mta_find_output(obj, 1); } } - + // Sanity check in case changes to code block above exits loop by invoking 'break' assert(zInputsDeque.size() == 0); assert(vpubNewProcessed); - + sign_send_raw_transaction(obj); return true; } extern UniValue signrawtransaction(const UniValue& params, bool fHelp); -extern UniValue sendrawtransaction(const UniValue& params, bool fHelp); /** * Sign and send a raw transaction. @@ -642,7 +778,7 @@ void AsyncRPCOperation_mergetoaddress::sign_send_raw_transaction(UniValue obj) throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for raw transaction"); } std::string rawtxn = rawtxnValue.get_str(); - + UniValue params = UniValue(UniValue::VARR); params.push_back(rawtxn); UniValue signResultValue = signrawtransaction(params, false); @@ -653,13 +789,13 @@ void AsyncRPCOperation_mergetoaddress::sign_send_raw_transaction(UniValue obj) // TODO: #1366 Maybe get "errors" and print array vErrors into a string throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Failed to sign transaction"); } - + UniValue hexValue = find_value(signResultObject, "hex"); if (hexValue.isNull()) { throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for signed transaction"); } std::string signedtxn = hexValue.get_str(); - + // Send the signed transaction if (!testmode) { params.clear(); @@ -669,26 +805,26 @@ void AsyncRPCOperation_mergetoaddress::sign_send_raw_transaction(UniValue obj) if (sendResultValue.isNull()) { throw JSONRPCError(RPC_WALLET_ERROR, "Send raw transaction did not return an error or a txid."); } - + std::string txid = sendResultValue.get_str(); - + UniValue o(UniValue::VOBJ); o.push_back(Pair("txid", txid)); set_result(o); } else { // Test mode does not send the transaction to the network. - + CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; stream >> tx; - + UniValue o(UniValue::VOBJ); o.push_back(Pair("test", 1)); o.push_back(Pair("txid", tx.GetHash().ToString())); o.push_back(Pair("hex", signedtxn)); set_result(o); } - + // Keep the signed transaction so we can hash to the same txid CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; @@ -721,106 +857,106 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(MergeToAddressJSInf } UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( - MergeToAddressJSInfo& info, - std::vector> witnesses, - uint256 anchor) + MergeToAddressJSInfo& info, + std::vector> witnesses, + uint256 anchor) { if (anchor.IsNull()) { throw std::runtime_error("anchor is null"); } - + if (witnesses.size() != info.notes.size()) { throw runtime_error("number of notes and witnesses do not match"); } - + if (info.notes.size() != info.zkeys.size()) { throw runtime_error("number of notes and spending keys do not match"); } - + for (size_t i = 0; i < witnesses.size(); i++) { if (!witnesses[i]) { throw runtime_error("joinsplit input could not be found in tree"); } info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], info.zkeys[i])); } - + // Make sure there are two inputs and two outputs while (info.vjsin.size() < ZC_NUM_JS_INPUTS) { info.vjsin.push_back(JSInput()); } - + while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) { info.vjsout.push_back(JSOutput()); } - + if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) { throw runtime_error("unsupported joinsplit input/output counts"); } - + CMutableTransaction mtx(tx_); - + LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", getId(), tx_.vjoinsplit.size(), FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), FormatMoney(info.vjsin[0].note.value()), FormatMoney(info.vjsin[1].note.value()), FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value)); - + // Generate the proof, this can take over a minute. std::array inputs{info.vjsin[0], info.vjsin[1]}; std::array outputs{info.vjsout[0], info.vjsout[1]}; std::array inputMap; std::array outputMap; - + uint256 esk; // payment disclosure - secret - + JSDescription jsdesc = JSDescription::Randomized( - mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION), - *pzcashParams, - joinSplitPubKey_, - anchor, - inputs, - outputs, - inputMap, - outputMap, - info.vpub_old, - info.vpub_new, - !this->testmode, - &esk); // parameter expects pointer to esk, so pass in address + mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION), + *pzcashParams, + joinSplitPubKey_, + anchor, + inputs, + outputs, + inputMap, + outputMap, + info.vpub_old, + info.vpub_new, + !this->testmode, + &esk); // parameter expects pointer to esk, so pass in address { auto verifier = libzcash::ProofVerifier::Strict(); if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { throw std::runtime_error("error verifying joinsplit"); } } - + mtx.vjoinsplit.push_back(jsdesc); - + // Empty output script. CScript scriptCode; CTransaction signTx(mtx); uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId_); - + // Add the signature if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, dataToBeSigned.begin(), 32, joinSplitPrivKey_) == 0)) { throw std::runtime_error("crypto_sign_detached failed"); } - + // Sanity check if (!(crypto_sign_verify_detached(&mtx.joinSplitSig[0], dataToBeSigned.begin(), 32, mtx.joinSplitPubKey.begin()) == 0)) { throw std::runtime_error("crypto_sign_verify_detached failed"); } - + CTransaction rawTx(mtx); tx_ = rawTx; - + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << rawTx; - + std::string encryptedNote1; std::string encryptedNote2; { @@ -829,7 +965,7 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ciphertexts[0]; ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); - + encryptedNote1 = HexStr(ss2.begin(), ss2.end()); } { @@ -838,10 +974,10 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ciphertexts[1]; ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); - + encryptedNote2 = HexStr(ss2.begin(), ss2.end()); } - + UniValue arrInputMap(UniValue::VARR); UniValue arrOutputMap(UniValue::VARR); for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) { @@ -850,8 +986,8 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { arrOutputMap.push_back(static_cast(outputMap[i])); } - - + + // !!! Payment disclosure START unsigned char buffer[32] = {0}; memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer @@ -867,11 +1003,11 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( libzcash::SproutPaymentAddress zaddr = output.addr; // randomized output PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); - + LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); } // !!! Payment disclosure END - + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("encryptednote1", encryptedNote1)); obj.push_back(Pair("encryptednote2", encryptedNote2)); @@ -884,19 +1020,19 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( std::array AsyncRPCOperation_mergetoaddress::get_memo_from_hex_string(std::string s) { std::array memo = {{0x00}}; - + std::vector rawMemo = ParseHex(s.c_str()); - + // If ParseHex comes across a non-hex char, it will stop but still return results so far. size_t slen = s.length(); if (slen % 2 != 0 || (slen > 0 && rawMemo.size() != slen / 2)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo must be in hexadecimal format"); } - + if (rawMemo.size() > ZC_MEMO_SIZE) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Memo size of %d is too big, maximum allowed is %d", rawMemo.size(), ZC_MEMO_SIZE)); } - + // copy vector into boost array int lenMemo = rawMemo.size(); for (int i = 0; i < ZC_MEMO_SIZE && i < lenMemo; i++) { @@ -914,7 +1050,7 @@ UniValue AsyncRPCOperation_mergetoaddress::getStatus() const if (contextinfo_.isNull()) { return v; } - + UniValue obj = v.get_obj(); obj.push_back(Pair("method", "z_mergetoaddress")); obj.push_back(Pair("params", contextinfo_)); @@ -924,7 +1060,7 @@ UniValue AsyncRPCOperation_mergetoaddress::getStatus() const /** * Lock input utxos */ - void AsyncRPCOperation_mergetoaddress::lock_utxos() { +void AsyncRPCOperation_mergetoaddress::lock_utxos() { LOCK2(cs_main, pwalletMain->cs_wallet); for (auto utxo : utxoInputs_) { pwalletMain->LockCoin(std::get<0>(utxo)); @@ -945,9 +1081,12 @@ void AsyncRPCOperation_mergetoaddress::unlock_utxos() { /** * Lock input notes */ - void AsyncRPCOperation_mergetoaddress::lock_notes() { +void AsyncRPCOperation_mergetoaddress::lock_notes() { LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto note : noteInputs_) { + for (auto note : sproutNoteInputs_) { + pwalletMain->LockNote(std::get<0>(note)); + } + for (auto note : saplingNoteInputs_) { pwalletMain->LockNote(std::get<0>(note)); } } @@ -957,7 +1096,10 @@ void AsyncRPCOperation_mergetoaddress::unlock_utxos() { */ void AsyncRPCOperation_mergetoaddress::unlock_notes() { LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto note : noteInputs_) { + for (auto note : sproutNoteInputs_) { + pwalletMain->UnlockNote(std::get<0>(note)); + } + for (auto note : saplingNoteInputs_) { pwalletMain->UnlockNote(std::get<0>(note)); } } diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.h b/src/wallet/asyncrpcoperation_mergetoaddress.h index ef59ba67b..f7ac81261 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.h +++ b/src/wallet/asyncrpcoperation_mergetoaddress.h @@ -9,6 +9,7 @@ #include "asyncrpcoperation.h" #include "paymentdisclosure.h" #include "primitives/transaction.h" +#include "transaction_builder.h" #include "wallet.h" #include "zcash/Address.hpp" #include "zcash/JoinSplit.hpp" @@ -24,11 +25,13 @@ using namespace libzcash; -// Input UTXO is a tuple of txid, vout, amount -typedef std::tuple MergeToAddressInputUTXO; +// Input UTXO is a tuple of txid, vout, amount, script +typedef std::tuple MergeToAddressInputUTXO; // Input JSOP is a tuple of JSOutpoint, note, amount, spending key -typedef std::tuple MergeToAddressInputNote; +typedef std::tuple MergeToAddressInputSproutNote; + +typedef std::tuple MergeToAddressInputSaplingNote; // A recipient is a tuple of address, memo (optional if zaddr) typedef std::tuple MergeToAddressRecipient; @@ -53,33 +56,36 @@ class AsyncRPCOperation_mergetoaddress : public AsyncRPCOperation { public: AsyncRPCOperation_mergetoaddress( - CMutableTransaction contextualTx, - std::vector utxoInputs, - std::vector noteInputs, - MergeToAddressRecipient recipient, - CAmount fee = MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE, - UniValue contextInfo = NullUniValue); + boost::optional builder, + CMutableTransaction contextualTx, + std::vector utxoInputs, + std::vector sproutNoteInputs, + std::vector saplingNoteInputs, + MergeToAddressRecipient recipient, + CAmount fee = MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE, + UniValue contextInfo = NullUniValue); virtual ~AsyncRPCOperation_mergetoaddress(); - + // We don't want to be copied or moved around AsyncRPCOperation_mergetoaddress(AsyncRPCOperation_mergetoaddress const&) = delete; // Copy construct AsyncRPCOperation_mergetoaddress(AsyncRPCOperation_mergetoaddress&&) = delete; // Move construct AsyncRPCOperation_mergetoaddress& operator=(AsyncRPCOperation_mergetoaddress const&) = delete; // Copy assign AsyncRPCOperation_mergetoaddress& operator=(AsyncRPCOperation_mergetoaddress&&) = delete; // Move assign - + virtual void main(); - + virtual UniValue getStatus() const; - + bool testmode = false; // Set to true to disable sending txs and generating proofs - - bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database. - + + bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database. + private: friend class TEST_FRIEND_AsyncRPCOperation_mergetoaddress; // class for unit testing - + UniValue contextinfo_; // optional data to include in return value from getStatus() - + + bool isUsingBuilder_; // Indicates that no Sprout addresses are involved uint32_t consensusBranchId_; CAmount fee_; int mindepth_; @@ -88,43 +94,45 @@ private: bool isToZaddr_; CTxDestination toTaddr_; PaymentAddress toPaymentAddress_; - + uint256 joinSplitPubKey_; unsigned char joinSplitPrivKey_[crypto_sign_SECRETKEYBYTES]; - + // The key is the result string from calling JSOutPoint::ToString() std::unordered_map jsopWitnessAnchorMap; - + std::vector utxoInputs_; - std::vector noteInputs_; - + std::vector sproutNoteInputs_; + std::vector saplingNoteInputs_; + + TransactionBuilder builder_; CTransaction tx_; - + std::array get_memo_from_hex_string(std::string s); bool main_impl(); - + // JoinSplit without any input notes to spend UniValue perform_joinsplit(MergeToAddressJSInfo&); - + // JoinSplit with input notes to spend (JSOutPoints)) UniValue perform_joinsplit(MergeToAddressJSInfo&, std::vector&); - + // JoinSplit where you have the witnesses and anchor UniValue perform_joinsplit( - MergeToAddressJSInfo& info, - std::vector> witnesses, - uint256 anchor); - + MergeToAddressJSInfo& info, + std::vector> witnesses, + uint256 anchor); + void sign_send_raw_transaction(UniValue obj); // throws exception if there was an error - + void lock_utxos(); - + void unlock_utxos(); - + void lock_notes(); - + void unlock_notes(); - + // payment disclosure! std::vector paymentDisclosureData_; }; @@ -135,54 +143,54 @@ class TEST_FRIEND_AsyncRPCOperation_mergetoaddress { public: std::shared_ptr delegate; - + TEST_FRIEND_AsyncRPCOperation_mergetoaddress(std::shared_ptr ptr) : delegate(ptr) {} - + CTransaction getTx() { return delegate->tx_; } - + void setTx(CTransaction tx) { delegate->tx_ = tx; } - + // Delegated methods - + std::array get_memo_from_hex_string(std::string s) { return delegate->get_memo_from_hex_string(s); } - + bool main_impl() { return delegate->main_impl(); } - + UniValue perform_joinsplit(MergeToAddressJSInfo& info) { return delegate->perform_joinsplit(info); } - + UniValue perform_joinsplit(MergeToAddressJSInfo& info, std::vector& v) { return delegate->perform_joinsplit(info, v); } - + UniValue perform_joinsplit( - MergeToAddressJSInfo& info, - std::vector> witnesses, - uint256 anchor) + MergeToAddressJSInfo& info, + std::vector> witnesses, + uint256 anchor) { return delegate->perform_joinsplit(info, witnesses, anchor); } - + void sign_send_raw_transaction(UniValue obj) { delegate->sign_send_raw_transaction(obj); } - + void set_state(OperationStatus state) { delegate->state_.store(state); diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 4e2895675..73288142a 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -123,7 +123,7 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( // Enable payment disclosure if requested - paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", false); + paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true); } AsyncRPCOperation_sendmany::~AsyncRPCOperation_sendmany() { @@ -1366,7 +1366,8 @@ void AsyncRPCOperation_sendmany::add_taddr_change_output_to_tx(CBitcoinAddress * } std::array AsyncRPCOperation_sendmany::get_memo_from_hex_string(std::string s) { - std::array memo = {{0x00}}; + // initialize to default memo (no_memo), see section 5.5 of the protocol spec + std::array memo = {{0xF6}}; std::vector rawMemo = ParseHex(s.c_str()); diff --git a/src/wallet/asyncrpcoperation_sendmany.h b/src/wallet/asyncrpcoperation_sendmany.h index 35bdb9740..a93925874 100644 --- a/src/wallet/asyncrpcoperation_sendmany.h +++ b/src/wallet/asyncrpcoperation_sendmany.h @@ -75,7 +75,7 @@ public: bool testmode = false; // Set to true to disable sending txs and generating proofs - bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database. + bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database. private: friend class TEST_FRIEND_AsyncRPCOperation_sendmany; // class for unit testing diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index dfc51aa3a..37c771dcb 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -93,7 +93,7 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase( lock_utxos(); // Enable payment disclosure if requested - paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", false); + paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true); } AsyncRPCOperation_shieldcoinbase::~AsyncRPCOperation_shieldcoinbase() { diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.h b/src/wallet/asyncrpcoperation_shieldcoinbase.h index 5f22a89fc..e75bb8e82 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.h +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.h @@ -65,7 +65,7 @@ public: bool testmode = false; // Set to true to disable sending txs and generating proofs bool cheatSpend = false; // set when this is shielding a cheating coinbase - bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database. + bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database. private: friend class ShieldToAddress; diff --git a/src/wallet/rpcdisclosure.cpp b/src/wallet/rpcdisclosure.cpp index dd3acab7e..41195cfc8 100644 --- a/src/wallet/rpcdisclosure.cpp +++ b/src/wallet/rpcdisclosure.cpp @@ -42,7 +42,7 @@ UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp) return NullUniValue; string enableArg = "paymentdisclosure"; - auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, false); + auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, true); string strPaymentDisclosureDisabledMsg = ""; if (!fEnablePaymentDisclosure) { strPaymentDisclosureDisabledMsg = experimentalDisabledHelpMsg("z_getpaymentdisclosure", enableArg); @@ -149,7 +149,7 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) return NullUniValue; string enableArg = "paymentdisclosure"; - auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, false); + auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, true); string strPaymentDisclosureDisabledMsg = ""; if (!fEnablePaymentDisclosure) { strPaymentDisclosureDisabledMsg = experimentalDisabledHelpMsg("z_validatepaymentdisclosure", enableArg); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index c65bdd746..9025d4597 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2342,6 +2342,7 @@ UniValue walletlock(const UniValue& params, bool fHelp) return NullUniValue; } +int32_t komodo_acpublic(uint32_t tiptime); UniValue encryptwallet(const UniValue& params, bool fHelp) { @@ -2349,7 +2350,8 @@ UniValue encryptwallet(const UniValue& params, bool fHelp) return NullUniValue; string enableArg = "developerencryptwallet"; - auto fEnableWalletEncryption = fExperimentalMode && GetBoolArg("-" + enableArg, false); + int32_t flag = (komodo_acpublic(0) || ASSETCHAINS_SYMBOL[0] == 0); + auto fEnableWalletEncryption = fExperimentalMode && GetBoolArg("-" + enableArg, flag); std::string strWalletEncryptionDisabledMsg = ""; if (!fEnableWalletEncryption) { @@ -2849,8 +2851,8 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) "\nExamples\n" + HelpExampleCli("z_listunspent", "") - + HelpExampleCli("z_listunspent", "6 9999999 false \"[\\\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\\\",\\\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\\\"]\"") - + HelpExampleRpc("z_listunspent", "6 9999999 false \"[\\\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\\\",\\\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\\\"]\"") + + HelpExampleCli("z_listunspent", "6 9999999 false \"[\\\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\\\",\\\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\\\"]\"") + + HelpExampleRpc("z_listunspent", "6 9999999 false \"[\\\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\\\",\\\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\\\"]\"") ); RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VBOOL)(UniValue::VARR)); @@ -3686,8 +3688,8 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) " \"change\": true|false, (boolean) true if the address that received the note is also one of the sending addresses\n" "}\n" "\nExamples:\n" - + HelpExampleCli("z_listreceivedbyaddress", "\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") - + HelpExampleRpc("z_listreceivedbyaddress", "\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") + + HelpExampleCli("z_listreceivedbyaddress", "\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\"") + + HelpExampleRpc("z_listreceivedbyaddress", "\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\"") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -4019,8 +4021,8 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) "\nResult:\n" "\"operationid\" (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" "\nExamples:\n" - + HelpExampleCli("z_sendmany", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" '[{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\" ,\"amount\": 5.0}]'") - + HelpExampleRpc("z_sendmany", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", [{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\" ,\"amount\": 5.0}]") + + HelpExampleCli("z_sendmany", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" '[{\"address\": \"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\" ,\"amount\": 5.0}]'") + + HelpExampleRpc("z_sendmany", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", [{\"address\": \"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\" ,\"amount\": 5.0}]") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -4110,15 +4112,10 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) } // If we are sending from a shielded address, all recipient // shielded addresses must be of the same type. - if (fromSprout && toSapling) { + if ((fromSprout && toSapling) || (fromSapling && toSprout)) { throw JSONRPCError( RPC_INVALID_PARAMETER, - "Cannot send from a Sprout address to a Sapling address using z_sendmany"); - } - if (fromSapling && toSprout) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Cannot send from a Sapling address to a Sprout address using z_sendmany"); + "Cannot send between Sprout and Sapling addresses using z_sendmany"); } } else { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address ); @@ -4330,8 +4327,8 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" "}\n" "\nExamples:\n" - + HelpExampleCli("z_shieldcoinbase", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") - + HelpExampleRpc("z_shieldcoinbase", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") + + HelpExampleCli("z_shieldcoinbase", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" \"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\"") + + HelpExampleRpc("z_shieldcoinbase", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", \"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\"") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -4519,160 +4516,159 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) return o; } - #define MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT 50 -#define MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT 10 +#define MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT 10 +#define MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT 90 #define JOINSPLIT_SIZE GetSerializeSize(JSDescription(), SER_NETWORK, PROTOCOL_VERSION) +#define OUTPUTDESCRIPTION_SIZE GetSerializeSize(OutputDescription(), SER_NETWORK, PROTOCOL_VERSION) +#define SPENDDESCRIPTION_SIZE GetSerializeSize(SpendDescription(), SER_NETWORK, PROTOCOL_VERSION) UniValue z_mergetoaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + string enableArg = "zmergetoaddress"; - auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-" + enableArg, false); + auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-" + enableArg, true); std::string strDisabledMsg = ""; if (!fEnableMergeToAddress) { strDisabledMsg = experimentalDisabledHelpMsg("z_mergetoaddress", enableArg); } - + if (fHelp || params.size() < 2 || params.size() > 6) throw runtime_error( - "z_mergetoaddress [\"fromaddress\", ... ] \"toaddress\" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo )\n" - + strDisabledMsg + - "\nMerge multiple UTXOs and notes into a single UTXO or note. Coinbase UTXOs are ignored; use `z_shieldcoinbase`" - "\nto combine those into a single note." - "\n\nThis is an asynchronous operation, and UTXOs selected for merging will be locked. If there is an error, they" - "\nare unlocked. The RPC call `listlockunspent` can be used to return a list of locked UTXOs." - "\n\nThe number of UTXOs and notes selected for merging can be limited by the caller. If the transparent limit" - "\nparameter is set to zero, and Overwinter is not yet active, the -mempooltxinputlimit option will determine the" - "\nnumber of UTXOs. Any limit is constrained by the consensus rule defining a maximum transaction size of" - + strprintf("\n%d bytes before Sapling, and %d bytes once Sapling activates.", MAX_TX_SIZE_BEFORE_SAPLING, MAX_TX_SIZE_AFTER_SAPLING) - + HelpRequiringPassphrase() + "\n" - "\nArguments:\n" - "1. fromaddresses (string, required) A JSON array with addresses.\n" - " The following special strings are accepted inside the array:\n" - " - \"*\": Merge both UTXOs and notes from all addresses belonging to the wallet.\n" - " - \"ANY_TADDR\": Merge UTXOs from all t-addrs belonging to the wallet.\n" - " - \"ANY_ZADDR\": Merge notes from all z-addrs belonging to the wallet.\n" - " If a special string is given, any given addresses of that type will be ignored.\n" - " [\n" - " \"address\" (string) Can be a t-addr or a z-addr\n" - " ,...\n" - " ]\n" - "2. \"toaddress\" (string, required) The t-addr or z-addr to send the funds to.\n" - "3. fee (numeric, optional, default=" - + strprintf("%s", FormatMoney(MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" - "4. transparent_limit (numeric, optional, default=" - + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT) + ") Limit on the maximum number of UTXOs to merge. Set to 0 to use node option -mempooltxinputlimit (before Overwinter), or as many as will fit in the transaction (after Overwinter).\n" - "4. shielded_limit (numeric, optional, default=" - + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT) + ") Limit on the maximum number of notes to merge. Set to 0 to merge as many as will fit in the transaction.\n" - "5. \"memo\" (string, optional) Encoded as hex. When toaddress is a z-addr, this will be stored in the memo field of the new note.\n" - "\nResult:\n" - "{\n" - " \"remainingUTXOs\": xxx (numeric) Number of UTXOs still available for merging.\n" - " \"remainingTransparentValue\": xxx (numeric) Value of UTXOs still available for merging.\n" - " \"remainingNotes\": xxx (numeric) Number of notes still available for merging.\n" - " \"remainingShieldedValue\": xxx (numeric) Value of notes still available for merging.\n" - " \"mergingUTXOs\": xxx (numeric) Number of UTXOs being merged.\n" - " \"mergingTransparentValue\": xxx (numeric) Value of UTXOs being merged.\n" - " \"mergingNotes\": xxx (numeric) Number of notes being merged.\n" - " \"mergingShieldedValue\": xxx (numeric) Value of notes being merged.\n" - " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("z_mergetoaddress", "'[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"]' ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf") - + HelpExampleRpc("z_mergetoaddress", "[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"], \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") - ); - + "z_mergetoaddress [\"fromaddress\", ... ] \"toaddress\" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo )\n" + + strDisabledMsg + + "\nMerge multiple UTXOs and notes into a single UTXO or note. Coinbase UTXOs are ignored; use `z_shieldcoinbase`" + "\nto combine those into a single note." + "\n\nThis is an asynchronous operation, and UTXOs selected for merging will be locked. If there is an error, they" + "\nare unlocked. The RPC call `listlockunspent` can be used to return a list of locked UTXOs." + "\n\nThe number of UTXOs and notes selected for merging can be limited by the caller. If the transparent limit" + "\nparameter is set to zero, and Overwinter is not yet active, the -mempooltxinputlimit option will determine the" + "\nnumber of UTXOs. Any limit is constrained by the consensus rule defining a maximum transaction size of" + + strprintf("\n%d bytes before Sapling, and %d bytes once Sapling activates.", MAX_TX_SIZE_BEFORE_SAPLING, MAX_TX_SIZE_AFTER_SAPLING) + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. fromaddresses (string, required) A JSON array with addresses.\n" + " The following special strings are accepted inside the array:\n" + " - \"ANY_TADDR\": Merge UTXOs from any t-addrs belonging to the wallet.\n" + " - \"ANY_SPROUT\": Merge notes from any Sprout z-addrs belonging to the wallet.\n" + " - \"ANY_SAPLING\": Merge notes from any Sapling z-addrs belonging to the wallet.\n" + " If a special string is given, any given addresses of that type will be ignored.\n" + " [\n" + " \"address\" (string) Can be a t-addr or a z-addr\n" + " ,...\n" + " ]\n" + "2. \"toaddress\" (string, required) The t-addr or z-addr to send the funds to.\n" + "3. fee (numeric, optional, default=" + + strprintf("%s", FormatMoney(MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" + "4. transparent_limit (numeric, optional, default=" + + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT) + ") Limit on the maximum number of UTXOs to merge. Set to 0 to use node option -mempooltxinputlimit (before Overwinter), or as many as will fit in the transaction (after Overwinter).\n" + "4. shielded_limit (numeric, optional, default=" + + strprintf("%d Sprout or %d Sapling Notes", MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT, MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT) + ") Limit on the maximum number of notes to merge. Set to 0 to merge as many as will fit in the transaction.\n" + "5. \"memo\" (string, optional) Encoded as hex. When toaddress is a z-addr, this will be stored in the memo field of the new note.\n" + "\nResult:\n" + "{\n" + " \"remainingUTXOs\": xxx (numeric) Number of UTXOs still available for merging.\n" + " \"remainingTransparentValue\": xxx (numeric) Value of UTXOs still available for merging.\n" + " \"remainingNotes\": xxx (numeric) Number of notes still available for merging.\n" + " \"remainingShieldedValue\": xxx (numeric) Value of notes still available for merging.\n" + " \"mergingUTXOs\": xxx (numeric) Number of UTXOs being merged.\n" + " \"mergingTransparentValue\": xxx (numeric) Value of UTXOs being merged.\n" + " \"mergingNotes\": xxx (numeric) Number of notes being merged.\n" + " \"mergingShieldedValue\": xxx (numeric) Value of notes being merged.\n" + " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("z_mergetoaddress", "'[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"]' ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf") + + HelpExampleRpc("z_mergetoaddress", "[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"], \"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\"") + ); + if (!fEnableMergeToAddress) { throw JSONRPCError(RPC_WALLET_ERROR, "Error: z_mergetoaddress is disabled."); } - + LOCK2(cs_main, pwalletMain->cs_wallet); - - bool useAny = false; + bool useAnyUTXO = false; - bool useAnyNote = false; + bool useAnySprout = false; + bool useAnySapling = false; std::set taddrs = {}; std::set zaddrs = {}; - - uint32_t branchId = CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()); - + UniValue addresses = params[0].get_array(); if (addresses.size()==0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, fromaddresses array is empty."); - + // Keep track of addresses to spot duplicates std::set setAddress; - + // Sources - bool containsSaplingZaddrSource = false; for (const UniValue& o : addresses.getValues()) { if (!o.isStr()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected string"); - + std::string address = o.get_str(); - if (address == "*") { - useAny = true; - } else if (address == "ANY_TADDR") { + + if (address == "ANY_TADDR") { useAnyUTXO = true; - } else if (address == "ANY_ZADDR") { - useAnyNote = true; + } else if (address == "ANY_SPROUT") { + useAnySprout = true; + } else if (address == "ANY_SAPLING") { + useAnySapling = true; } else { CTxDestination taddr = DecodeDestination(address); if (IsValidDestination(taddr)) { - // Ignore any listed t-addrs if we are using all of them - if (!(useAny || useAnyUTXO)) { - taddrs.insert(taddr); - } + taddrs.insert(taddr); } else { auto zaddr = DecodePaymentAddress(address); - if (IsValidPaymentAddress(zaddr, branchId)) { - // Ignore listed z-addrs if we are using all of them - if (!(useAny || useAnyNote)) { - zaddrs.insert(zaddr); - } - // Check if z-addr is Sapling - bool isSapling = boost::get(&zaddr) != nullptr; - containsSaplingZaddrSource |= isSapling; + if (IsValidPaymentAddress(zaddr)) { + zaddrs.insert(zaddr); } else { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - string("Invalid parameter, unknown address format: ") + address); + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Unknown address format: ") + address); } } } - + if (setAddress.count(address)) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + address); setAddress.insert(address); } - + + if (useAnyUTXO && taddrs.size() > 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific t-addrs when using \"ANY_TADDR\""); + } + if ((useAnySprout || useAnySapling) && zaddrs.size() > 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific z-addrs when using \"ANY_SPROUT\" or \"ANY_SAPLING\""); + } + + const int nextBlockHeight = chainActive.Height() + 1; + const bool overwinterActive = NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); + const bool saplingActive = NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING); + // Validate the destination address auto destaddress = params[1].get_str(); - bool isToZaddr = false; + bool isToSproutZaddr = false; bool isToSaplingZaddr = false; CTxDestination taddr = DecodeDestination(destaddress); if (!IsValidDestination(taddr)) { - if (IsValidPaymentAddressString(destaddress, branchId)) { - isToZaddr = true; - - // Is this a Sapling address? - auto res = DecodePaymentAddress(destaddress); - if (IsValidPaymentAddress(res)) { - isToSaplingZaddr = boost::get(&res) != nullptr; + auto decodeAddr = DecodePaymentAddress(destaddress); + if (IsValidPaymentAddress(decodeAddr)) { + if (boost::get(&decodeAddr) != nullptr) { + isToSaplingZaddr = true; + // If Sapling is not active, do not allow sending to a sapling addresses. + if (!saplingActive) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); + } } else { - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); + isToSproutZaddr = true; } } else { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); } } - else if ( ASSETCHAINS_PRIVATE != 0 ) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cant use transparent addresses in private chain"); - + // Convert fee from currency format to zatoshis CAmount nFee = SHIELD_COINBASE_DEFAULT_MINERS_FEE; if (params.size() > 2) { @@ -4682,7 +4678,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) nFee = AmountFromValue( params[2] ); } } - + int nUTXOLimit = MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT; if (params.size() > 3) { nUTXOLimit = params[3].get_int(); @@ -4690,19 +4686,22 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of UTXOs cannot be negative"); } } - - int nNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT; + + int sproutNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT; + int saplingNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT; if (params.size() > 4) { - nNoteLimit = params[4].get_int(); + int nNoteLimit = params[4].get_int(); if (nNoteLimit < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative"); } + sproutNoteLimit = nNoteLimit; + saplingNoteLimit = nNoteLimit; } - + std::string memo; if (params.size() > 5) { memo = params[5].get_str(); - if (!isToZaddr) { + if (!(isToSproutZaddr || isToSaplingZaddr)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr."); } else if (!IsHex(memo)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); @@ -4711,75 +4710,56 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE )); } } - + MergeToAddressRecipient recipient(destaddress, memo); - - int nextBlockHeight = chainActive.Height() + 1; - bool overwinterActive = NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); - unsigned int max_tx_size = MAX_TX_SIZE_AFTER_SAPLING; - if (!NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { - max_tx_size = MAX_TX_SIZE_BEFORE_SAPLING; - } - - // This RPC does not support Sapling yet. - if (isToSaplingZaddr || containsSaplingZaddrSource) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling is not supported yet by z_mergetoadress"); - } - - // If this RPC does support Sapling... - // If Sapling is not active, do not allow sending from or sending to Sapling addresses. - if (!NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { - if (isToSaplingZaddr || containsSaplingZaddrSource) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); - } - } - + // Prepare to get UTXOs and notes std::vector utxoInputs; - std::vector noteInputs; + std::vector sproutNoteInputs; + std::vector saplingNoteInputs; CAmount mergedUTXOValue = 0; CAmount mergedNoteValue = 0; CAmount remainingUTXOValue = 0; CAmount remainingNoteValue = 0; - #ifdef __LP64__ - uint64_t utxoCounter = 0; - uint64_t noteCounter = 0; - #else size_t utxoCounter = 0; size_t noteCounter = 0; - #endif bool maxedOutUTXOsFlag = false; bool maxedOutNotesFlag = false; size_t mempoolLimit = (nUTXOLimit != 0) ? nUTXOLimit : (overwinterActive ? 0 : (size_t)GetArg("-mempooltxinputlimit", 0)); - + + unsigned int max_tx_size = saplingActive ? MAX_TX_SIZE_AFTER_SAPLING : MAX_TX_SIZE_BEFORE_SAPLING; size_t estimatedTxSize = 200; // tx overhead + wiggle room - if (isToZaddr) { + if (isToSproutZaddr) { estimatedTxSize += JOINSPLIT_SIZE; + } else if (isToSaplingZaddr) { + estimatedTxSize += OUTPUTDESCRIPTION_SIZE; } - - if (useAny || useAnyUTXO || taddrs.size() > 0) { + + if (useAnyUTXO || taddrs.size() > 0) { // Get available utxos vector vecOutputs; pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, false); - + // Find unspent utxos and update estimated size for (const COutput& out : vecOutputs) { if (!out.fSpendable) { continue; } - + + CScript scriptPubKey = out.tx->vout[out.i].scriptPubKey; + CTxDestination address; - if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { + if (!ExtractDestination(scriptPubKey, address)) { continue; } // If taddr is not wildcard "*", filter utxos if (taddrs.size() > 0 && !taddrs.count(address)) { continue; } - + utxoCounter++; CAmount nValue = out.tx->vout[out.i].nValue; - + if (!maxedOutUTXOsFlag) { size_t increase = (boost::get(&address) != nullptr) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE; if (estimatedTxSize + increase >= max_tx_size || @@ -4789,34 +4769,51 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) } else { estimatedTxSize += increase; COutPoint utxo(out.tx->GetHash(), out.i); - utxoInputs.emplace_back(utxo, nValue); + utxoInputs.emplace_back(utxo, nValue, scriptPubKey); mergedUTXOValue += nValue; } } - + if (maxedOutUTXOsFlag) { remainingUTXOValue += nValue; } } } - - if (useAny || useAnyNote || zaddrs.size() > 0) { + + if (useAnySprout || useAnySapling || zaddrs.size() > 0) { // Get available notes std::vector sproutEntries; std::vector saplingEntries; pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs); - + + // If Sapling is not active, do not allow sending from a sapling addresses. + if (!saplingActive && saplingEntries.size() > 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); + } + // Sending from both Sprout and Sapling is currently unsupported using z_mergetoaddress + if (sproutEntries.size() > 0 && saplingEntries.size() > 0) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress"); + } + // If sending between shielded addresses, they must be the same type + if ((saplingEntries.size() > 0 && isToSproutZaddr) || (sproutEntries.size() > 0 && isToSaplingZaddr)) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "Cannot send between Sprout and Sapling addresses using z_mergetoaddress"); + } + // Find unspent notes and update estimated size - for (CSproutNotePlaintextEntry& entry : sproutEntries) { + for (const CSproutNotePlaintextEntry& entry : sproutEntries) { noteCounter++; CAmount nValue = entry.plaintext.value(); - + if (!maxedOutNotesFlag) { // If we haven't added any notes yet and the merge is to a // z-address, we have already accounted for the first JoinSplit. - size_t increase = (noteInputs.empty() && !isToZaddr) || (noteInputs.size() % 2 == 0) ? JOINSPLIT_SIZE : 0; + size_t increase = (sproutNoteInputs.empty() && !isToSproutZaddr) || (sproutNoteInputs.size() % 2 == 0) ? JOINSPLIT_SIZE : 0; if (estimatedTxSize + increase >= max_tx_size || - (nNoteLimit > 0 && noteCounter > nNoteLimit)) + (sproutNoteLimit > 0 && noteCounter > sproutNoteLimit)) { maxedOutNotesFlag = true; } else { @@ -4824,31 +4821,49 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) auto zaddr = entry.address; SproutSpendingKey zkey; pwalletMain->GetSproutSpendingKey(zaddr, zkey); - noteInputs.emplace_back(entry.jsop, entry.plaintext.note(zaddr), nValue, zkey); + sproutNoteInputs.emplace_back(entry.jsop, entry.plaintext.note(zaddr), nValue, zkey); mergedNoteValue += nValue; } } - + + if (maxedOutNotesFlag) { + remainingNoteValue += nValue; + } + } + + for (const SaplingNoteEntry& entry : saplingEntries) { + noteCounter++; + CAmount nValue = entry.note.value(); + if (!maxedOutNotesFlag) { + size_t increase = SPENDDESCRIPTION_SIZE; + if (estimatedTxSize + increase >= max_tx_size || + (saplingNoteLimit > 0 && noteCounter > saplingNoteLimit)) + { + maxedOutNotesFlag = true; + } else { + estimatedTxSize += increase; + libzcash::SaplingExtendedSpendingKey extsk; + if (!pwalletMain->GetSaplingExtendedSpendingKey(entry.address, extsk)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find spending key for payment address."); + } + saplingNoteInputs.emplace_back(entry.op, entry.note, nValue, extsk.expsk); + mergedNoteValue += nValue; + } + } + if (maxedOutNotesFlag) { remainingNoteValue += nValue; } } - // TODO: Add Sapling support } - - #ifdef __LP64__ - uint64_t numUtxos = utxoInputs.size(); //ca333 - uint64_t numNotes = noteInputs.size(); - #else + size_t numUtxos = utxoInputs.size(); - size_t numNotes = noteInputs.size(); - #endif - - + size_t numNotes = sproutNoteInputs.size() + saplingNoteInputs.size(); + if (numUtxos == 0 && numNotes == 0) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any funds to merge."); } - + // Sanity check: Don't do anything if: // - We only have one from address // - It's equal to toaddress @@ -4856,42 +4871,47 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) if (setAddress.size() == 1 && setAddress.count(destaddress) && (numUtxos + numNotes) == 1) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Destination address is also the only source address, and all its funds are already merged."); } - + CAmount mergedValue = mergedUTXOValue + mergedNoteValue; if (mergedValue < nFee) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, - strprintf("Insufficient funds, have %s, which is less than miners fee %s", - FormatMoney(mergedValue), FormatMoney(nFee))); + strprintf("Insufficient funds, have %s, which is less than miners fee %s", + FormatMoney(mergedValue), FormatMoney(nFee))); } - + // Check that the user specified fee is sane (if too high, it can result in error -25 absurd fee) CAmount netAmount = mergedValue - nFee; if (nFee > netAmount) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the net amount to be shielded %s", FormatMoney(nFee), FormatMoney(netAmount))); } - + // Keep record of parameters in context object UniValue contextInfo(UniValue::VOBJ); contextInfo.push_back(Pair("fromaddresses", params[0])); contextInfo.push_back(Pair("toaddress", params[1])); contextInfo.push_back(Pair("fee", ValueFromAmount(nFee))); - + // Contextual transaction we will build on CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( - Params().GetConsensus(), - nextBlockHeight); - bool isShielded = numNotes > 0 || isToZaddr; - if (contextualTx.nVersion == 1 && isShielded) { + Params().GetConsensus(), + nextBlockHeight); + bool isSproutShielded = sproutNoteInputs.size() > 0 || isToSproutZaddr; + if (contextualTx.nVersion == 1 && isSproutShielded) { contextualTx.nVersion = 2; // Tx format should support vjoinsplit } - + + // Builder (used if Sapling addresses are involved) + boost::optional builder; + if (isToSaplingZaddr || saplingNoteInputs.size() > 0) { + builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); + } // Create operation and add to global queue std::shared_ptr q = getAsyncRPCQueue(); std::shared_ptr operation( - new AsyncRPCOperation_mergetoaddress(contextualTx, utxoInputs, noteInputs, recipient, nFee, contextInfo) ); + new AsyncRPCOperation_mergetoaddress(builder, contextualTx, utxoInputs, sproutNoteInputs, saplingNoteInputs, recipient, nFee, contextInfo) ); q->addOperation(operation); AsyncRPCOperationId operationId = operation->getId(); - + // Return continuation information UniValue o(UniValue::VOBJ); o.push_back(Pair("remainingUTXOs", static_cast(utxoCounter - numUtxos))); @@ -5059,8 +5079,10 @@ struct komodo_staking *komodo_addutxo(struct komodo_staking *array,int32_t *numk { *maxkp += 1000; array = (struct komodo_staking *)realloc(array,sizeof(*array) * (*maxkp)); + //fprintf(stderr,"realloc max.%d array.%p\n",*maxkp,array); } kp = &array[(*numkp)++]; + //fprintf(stderr,"kp.%p num.%d\n",kp,*numkp); memset(kp,0,sizeof(*kp)); strcpy(kp->address,address); kp->txid = txid; @@ -5083,15 +5105,11 @@ arith_uint256 _komodo_eligible(struct komodo_staking *kp,arith_uint256 ratio,uin diff = 3600*24*30; if ( iter > 0 ) diff += segid*2; - coinage = ((uint64_t)kp->nValue/COIN * diff); + coinage = ((uint64_t)kp->nValue * diff); if ( blocktime+iter+segid*2 > prevtime+480 ) coinage *= ((blocktime+iter+segid*2) - (prevtime+400)); - //if ( nHeight >= 2500 && blocktime+iter+segid*2 > prevtime+180 ) - // coinage *= ((blocktime+iter+segid*2) - (prevtime+60)); coinage256 = arith_uint256(coinage+1); hashval = ratio * (kp->hashval / coinage256); - //if ( nHeight >= 900 && nHeight < 916 ) - // hashval = (hashval / coinage256); return(hashval); } @@ -5121,7 +5139,7 @@ uint32_t komodo_eligible(arith_uint256 bnTarget,arith_uint256 ratio,struct komod return(blocktime); } } - } + } else fprintf(stderr,"maxiters is not good enough\n"); return(0); } @@ -5148,10 +5166,10 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt if ( (minage= nHeight*3) > 6000 ) // about 100 blocks minage = 6000; komodo_segids(hashbuf,nHeight-101,100); - if ( *blocktimep > tipindex->nTime+60 ) + if ( *blocktimep < tipindex->nTime+60 ) *blocktimep = tipindex->nTime+60; //fprintf(stderr,"Start scan of utxo for staking %u ht.%d\n",(uint32_t)time(NULL),nHeight); - if ( time(NULL) > lasttime+600 ) + if ( time(NULL) > lasttime+600 || array == 0 ) { if ( array != 0 ) { @@ -5170,7 +5188,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt counter++; if ( out.nDepth < nMinDepth || out.nDepth > nMaxDepth ) { - //fprintf(stderr,"komodo_staked invalid depth %d\n",(int32_t)out.nDepth); + fprintf(stderr,"komodo_staked invalid depth %d\n",(int32_t)out.nDepth); continue; } CAmount nValue = out.tx->vout[out.i].nValue; @@ -5184,12 +5202,14 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt if ( GetTransaction(out.tx->GetHash(),tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 ) { array = komodo_addutxo(array,&numkp,&maxkp,(uint32_t)pindex->nTime,(uint64_t)nValue,out.tx->GetHash(),out.i,(char *)CBitcoinAddress(address).ToString().c_str(),hashbuf,(CScript)pk); + //fprintf(stderr,"addutxo numkp.%d vs max.%d\n",numkp,maxkp); } } } lasttime = (uint32_t)time(NULL); - //fprintf(stderr,"finished kp data of utxo for staking %u ht.%d numkp.%d maxkp.%d\n",(uint32_t)time(NULL),nHeight,numkp,maxkp); +//fprintf(stderr,"finished kp data of utxo for staking %u ht.%d numkp.%d maxkp.%d\n",(uint32_t)time(NULL),nHeight,numkp,maxkp); } +//fprintf(stderr,"numkp.%d blocktime.%u\n",numkp,*blocktimep); block_from_future_rejecttime = (uint32_t)GetAdjustedTime() + 57; for (i=winners=0; inTime+27,minage,hashbuf)) == 0 ) continue; eligible = komodo_stake(0,bnTarget,nHeight,kp->txid,kp->vout,0,(uint32_t)tipindex->nTime+27,kp->address); - //fprintf(stderr,"i.%d %u vs %u\n",i,eligible2,eligible); +//fprintf(stderr,"i.%d %u vs %u\n",i,eligible2,eligible); if ( eligible > 0 ) { besttime = m = 0; @@ -5215,7 +5235,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt if ( eligible < block_from_future_rejecttime ) // nothing gained by going earlier break; m++; - //fprintf(stderr,"m.%d ht.%d validated winning blocktime %u -> %.8f eligible.%u test prior\n",m,nHeight,*blocktimep,(double)kp->nValue/COIN,eligible); +//fprintf(stderr,"m.%d ht.%d validated winning blocktime %u -> %.8f eligible.%u test prior\n",m,nHeight,*blocktimep,(double)kp->nValue/COIN,eligible); } } else @@ -5225,7 +5245,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt } eligible = besttime; winners++; - //fprintf(stderr,"ht.%d validated winning [%d] -> %.8f eligible.%u test prior\n",nHeight,(int32_t)(eligible - tipindex->nTime),(double)kp->nValue/COIN,eligible); +//fprintf(stderr,"ht.%d validated winning [%d] -> %.8f eligible.%u test prior\n",nHeight,(int32_t)(eligible - tipindex->nTime),(double)kp->nValue/COIN,eligible); if ( earliest == 0 || eligible < earliest || (eligible == earliest && (*utxovaluep == 0 || kp->nValue < *utxovaluep)) ) { earliest = eligible; @@ -5238,7 +5258,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt fprintf(stderr,"ht.%d earliest.%u [%d].%d (%s) nValue %.8f locktime.%u counter.%d winners.%d\n",nHeight,earliest,(int32_t)(earliest - tipindex->nTime),m,kp->address,(double)kp->nValue/COIN,*txtimep,counter,winners); } } //else fprintf(stderr,"utxo not eligible\n"); - } //else fprintf(stderr,"no tipindex\n"); + } if ( numkp < 10000 && array != 0 ) { free(array); @@ -5273,7 +5293,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt for (i=0; i done + if (ignoreLocked && IsLockedNote(op)) { + continue; + } auto note = notePt.note(nd.ivk).get(); saplingEntries.push_back(SaplingNoteEntry {